diff --git a/play-services-basement/src/main/java/org/microg/gms/common/Constants.java b/play-services-basement/src/main/java/org/microg/gms/common/Constants.java index c6ea9e002d..482e0f37b5 100644 --- a/play-services-basement/src/main/java/org/microg/gms/common/Constants.java +++ b/play-services-basement/src/main/java/org/microg/gms/common/Constants.java @@ -28,4 +28,5 @@ public class Constants { public static final String MICROG_PACKAGE_SIGNATURE_SHA1 = "10321bd893f69af97f7573aafe9de1dc0901f3a1"; @Deprecated public static final int MAX_REFERENCE_VERSION = GMS_VERSION_CODE; + public static final String VENDING_PACKAGE_NAME = "com.android.vending"; } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java index dd9a1be6ac..5d31983e14 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java @@ -22,6 +22,7 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; +import android.content.Intent; import android.graphics.Color; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -77,6 +78,7 @@ import static org.microg.gms.auth.AuthPrefs.isAuthVisible; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import static org.microg.gms.common.Constants.GMS_VERSION_CODE; +import static org.microg.gms.common.Constants.VENDING_PACKAGE_NAME; public class LoginActivity extends AssistantActivity { public static final String TMPL_NEW_ACCOUNT = "new_account"; @@ -91,6 +93,7 @@ public class LoginActivity extends AssistantActivity { private static final String GOOGLE_SUITE_URL = "https://accounts.google.com/signin/continue"; private static final String MAGIC_USER_AGENT = " MinuteMaid"; private static final String COOKIE_OAUTH_TOKEN = "oauth_token"; + private static final String ACTION_UPDATE_ACCOUNT = "com.google.android.gms.auth.GOOGLE_ACCOUNT_CHANGE"; private final FidoHandler fidoHandler = new FidoHandler(this); private final DroidGuardHandler dgHandler = new DroidGuardHandler(this); @@ -357,6 +360,10 @@ private void returnSuccessResponse(Account account){ bd.putString(AccountManager.KEY_ACCOUNT_TYPE,accountType); response.onResult(bd); } + Intent intent = new Intent(ACTION_UPDATE_ACCOUNT); + intent.setPackage(VENDING_PACKAGE_NAME); + intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name); + sendBroadcast(intent); } private void retrieveGmsToken(final Account account) { final AuthManager authManager = new AuthManager(this, account.name, GMS_PACKAGE_NAME, "ac2dm"); diff --git a/play-services-core/src/main/java/org/microg/tools/selfcheck/InstalledPackagesChecks.java b/play-services-core/src/main/java/org/microg/tools/selfcheck/InstalledPackagesChecks.java index 7b685fb6a1..59cfdf3175 100644 --- a/play-services-core/src/main/java/org/microg/tools/selfcheck/InstalledPackagesChecks.java +++ b/play-services-core/src/main/java/org/microg/tools/selfcheck/InstalledPackagesChecks.java @@ -42,7 +42,7 @@ public String getGroupName(Context context) { @Override public void doChecks(Context context, ResultCollector collector) { addPackageInstalledAndSignedResult(context, collector, context.getString(R.string.self_check_pkg_gms), Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1); - addPackageInstalledAndSignedResult(context, collector, context.getString(R.string.self_check_pkg_vending), "com.android.vending", Constants.GMS_PACKAGE_SIGNATURE_SHA1); + addPackageInstalledAndSignedResult(context, collector, context.getString(R.string.self_check_pkg_vending), Constants.VENDING_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1); addPackageInstalledResult(context, collector, context.getString(R.string.self_check_pkg_gsf), Constants.GSF_PACKAGE_NAME); } diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index eb12ae7378..26e6c61508 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -19,6 +19,9 @@ + + + + + + + + diff --git a/vending-app/src/main/kotlin/com/google/android/finsky/DeviceSyncInfo.kt b/vending-app/src/main/kotlin/com/google/android/finsky/DeviceSyncInfo.kt new file mode 100644 index 0000000000..a1aeb6241d --- /dev/null +++ b/vending-app/src/main/kotlin/com/google/android/finsky/DeviceSyncInfo.kt @@ -0,0 +1,612 @@ +/** + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.finsky + +import android.accounts.Account +import android.accounts.AccountManager +import android.annotation.SuppressLint +import android.app.ActivityManager +import android.app.admin.DevicePolicyManager +import android.content.Context +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.graphics.Point +import android.opengl.GLES10 +import android.os.Build +import android.telephony.TelephonyManager +import android.text.TextUtils +import android.util.Base64 +import android.util.DisplayMetrics +import android.util.Log +import android.view.WindowManager +import org.microg.gms.common.Constants +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import java.util.Arrays +import java.util.Objects +import java.util.Random +import java.util.TimeZone +import java.util.regex.Pattern +import java.util.stream.Collectors +import javax.microedition.khronos.egl.EGL10 +import javax.microedition.khronos.egl.EGLConfig +import javax.microedition.khronos.egl.EGLContext +import javax.microedition.khronos.egl.EGLDisplay +import javax.microedition.khronos.egl.EGLSurface +import kotlin.math.abs + +object DeviceSyncInfo { + + private const val TAG = "DeviceSyncInfo" + private val glInfoList = ArrayList() + + fun buildSyncRequest(context: Context, androidId: String, account: Account): SyncReqWrapper { + Log.d(TAG, "cachePayload: ") + val builder = SyncReqWrapper.Builder() + val payloads = buildPayloads(context, androidId, account) + val syncRequests = builder.request.toMutableList() + for (payload in payloads) { + payload?.run { syncRequests.add(this) } + } + builder.request = syncRequests + return builder.build() + } + + private fun buildPayloads(context: Context, androidId: String, account: Account): Array { + val fetchedGlStrings: ArrayList = fetchGLInfo() + //---------------------------------------GPU info-------------------------------------------------------------------- + val accountSha256 = accountSha256(androidId, account) + val accountAssValue = AccountAssValue.Builder().value_(accountSha256).build() + val accountAssociationPayload = AccountAssociationPayload.Builder().accountAss(accountAssValue).build() + val accountAssociationPayloadRequest = SyncRequest.Builder().accountAssociationPayload(accountAssociationPayload).build() + //-------------------------------------------------------------------------------------------------------------------- + val carrierPropertiesPayloadRequest = createCarrierPropertiesPayloadRequest(context) + val deviceAccountsPayloadRequest = createDeviceAccountsPayloadRequest(context, androidId) + val deviceInfoCollect = createDeviceInfoCollect(context, fetchedGlStrings.toList()) + val deviceCapabilitiesPayloadRequest = createDeviceCapabilitiesPayloadRequest(deviceInfoCollect) + val deviceInputPropertiesPayloadRequest = createDeviceInputPropertiesPayloadRequest(deviceInfoCollect) + val deviceModelPayloadRequest = createDeviceModelPayloadRequest() + val enterprisePropertiesPayloadRequest = createEnterprisePropertiesPayloadRequest(context) + val hardwareIdentifierPayloadRequest = createHardwareIdentifierPayloadRequest(context) + val hardwarePropertiesPayloadRequest = createHardwarePropertiesPayloadRequest(deviceInfoCollect) + val localePropertiesPayloadRequest = createLocalePropertiesPayloadRequest() + val playPartnerPropertiesPayloadRequest = createPlayPartnerPropertiesPayloadRequest() + val playPropertiesPayloadRequest = createPlayPropertiesPayload(context) + val screenPropertiesPayloadRequest = createScreenPropertiesPayloadRequest(deviceInfoCollect) + val systemPropertiesPayloadRequest = createSystemPropertiesPayloadRequest(deviceInfoCollect) + val gpuPayloadRequest = createGpuPayloadRequest(fetchedGlStrings.toList()) + return arrayOf( + accountAssociationPayloadRequest, carrierPropertiesPayloadRequest, deviceAccountsPayloadRequest, + deviceCapabilitiesPayloadRequest, deviceInputPropertiesPayloadRequest, deviceModelPayloadRequest, + enterprisePropertiesPayloadRequest, hardwareIdentifierPayloadRequest, hardwarePropertiesPayloadRequest, + localePropertiesPayloadRequest, playPartnerPropertiesPayloadRequest, playPropertiesPayloadRequest, + screenPropertiesPayloadRequest, systemPropertiesPayloadRequest, gpuPayloadRequest + ) + } + + private fun createDeviceInfoCollect(context: Context, gpuInfoList: List): DeviceInfoCollect { + val builder = DeviceInfoCollect.Builder() + .reqTouchScreen(0) + .reqKeyboardType(0) + .reqNavigation(0) + .deviceStablePoint(0) + .reqInputFeaturesV1(false) + .reqInputFeaturesV2(false) + .deviceStable(0) + .reqGlEsVersion(0) + val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val configurationInfo = activityManager.deviceConfigurationInfo + if (configurationInfo != null) { + if (configurationInfo.reqTouchScreen != Configuration.TOUCHSCREEN_UNDEFINED) { + builder.reqTouchScreen(configurationInfo.reqTouchScreen) + } + if (configurationInfo.reqKeyboardType != Configuration.KEYBOARD_UNDEFINED) { + builder.reqKeyboardType(configurationInfo.reqKeyboardType) + } + if (configurationInfo.reqNavigation != Configuration.NAVIGATION_UNDEFINED) { + builder.reqNavigation(configurationInfo.reqNavigation) + } + builder.reqGlEsVersion(configurationInfo.reqGlEsVersion) + builder.reqInputFeaturesV1((configurationInfo.reqInputFeatures and 1) == 1) + .reqInputFeaturesV2((configurationInfo.reqInputFeatures and 2) > 0) + } + val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val size = Point() + windowManager.defaultDisplay.getSize(size) + builder.displayX(size.x).displayY(size.y) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + builder.deviceStable(DisplayMetrics.DENSITY_DEVICE_STABLE) + .deviceStablePoint(calculatePoint(size, DisplayMetrics.DENSITY_DEVICE_STABLE)) + } + val configuration = context.resources.configuration + builder.screenLayout(configuration.screenLayout) + .smallestScreenWidthDp(configuration.smallestScreenWidthDp) + .systemSharedLibraryNames(listOf(*Objects.requireNonNull(context.packageManager.systemSharedLibraryNames))) + .locales(listOf(*context.assets.locales)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + builder.glExtensions(gpuInfoList.stream() + .flatMap { fetchedGlStrings: FetchedGlStrings -> fetchedGlStrings.glExtensions?.let { Arrays.stream(it.toTypedArray()) } } + .collect(Collectors.toList())) + .isLowRamDevice(activityManager.isLowRamDevice) + } + val memoryInfo = ActivityManager.MemoryInfo() + activityManager.getMemoryInfo(memoryInfo) + builder.totalMem(memoryInfo.totalMem) + .availableProcessors(Runtime.getRuntime().availableProcessors()) + val systemAvailableFeatures = context.packageManager.systemAvailableFeatures + for (featureInfo in systemAvailableFeatures) { + if (!TextUtils.isEmpty(featureInfo.name)) { + var featureInfoProto = FeatureInfoProto.Builder().build() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + featureInfoProto = FeatureInfoProto.Builder().name(featureInfo.name).version(featureInfo.version).build() + } + builder.featureInfoList = builder.featureInfoList.toMutableList().apply { + add(featureInfoProto) + } + builder.featureNames = builder.featureNames.toMutableList().apply { + add(featureInfoProto.name!!) + } + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder.supportedAbi(listOf(*Build.SUPPORTED_ABIS)) + } + var prop = getSystemProperty("ro.oem.key1", "") + if (!TextUtils.isEmpty(prop)) { + builder.oemKey(prop) + } + builder.buildCodeName(Build.VERSION.CODENAME) + prop = getSystemProperty("ro.build.version.preview_sdk_fingerprint", "") + if (!TextUtils.isEmpty(prop)) { + builder.previewSdkFingerprint(prop) + } + return builder.build() + } + + private fun createGpuPayloadRequest(glStringsList: List): SyncRequest? { + var gpuPayloadRequest: SyncRequest? = null + try { + var infos = glStringsList + var gpuPayloads = emptyList() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + infos = infos.stream() + .filter { fetchedGlStrings: FetchedGlStrings -> + fetchedGlStrings.glRenderer!!.isNotEmpty() || fetchedGlStrings.glVendor!!.isNotEmpty() || fetchedGlStrings.glVersion!!.isNotEmpty() + }.collect(Collectors.toList()) + val maxVersion = infos.stream() + .max(Comparator.comparingInt { fetchedGlStrings: FetchedGlStrings -> + fetchedGlStrings.contextClientVersion + }).map { obj: FetchedGlStrings -> + obj.contextClientVersion + } + if (maxVersion.isPresent) { + infos = infos.stream() + .filter { fetchedGlStrings: FetchedGlStrings -> + fetchedGlStrings.contextClientVersion == maxVersion.get() + }.collect(Collectors.toList()) + } + gpuPayloads = infos.stream().map { fetchedGlStrings: FetchedGlStrings -> + val gpuInfoWrapper = GpuInfoWrapper.Builder() + if (!TextUtils.isEmpty(fetchedGlStrings.glRenderer)) gpuInfoWrapper.glRenderer(fetchedGlStrings.glRenderer) + if (!TextUtils.isEmpty(fetchedGlStrings.glVendor)) gpuInfoWrapper.glVendor(fetchedGlStrings.glVendor) + if (!TextUtils.isEmpty(fetchedGlStrings.glVersion)) gpuInfoWrapper.glVersion(fetchedGlStrings.glVersion) + GpuPayload.Builder().gpuInfo(gpuInfoWrapper.build()).build() + }.distinct().collect(Collectors.toList()) + } + gpuPayloadRequest = SyncRequest.Builder().gpuPayload(if (gpuPayloads.isEmpty()) GpuPayload.Builder().build() else gpuPayloads[0]).build() + } catch (e: Exception) { + Log.w(TAG, "createGpuPayloadRequest error", e) + } + return gpuPayloadRequest + } + + private fun createHardwarePropertiesPayloadRequest(deviceInfoCollect: DeviceInfoCollect): SyncRequest { + val hardwarePropertiesPayload = HardwarePropertiesPayload.Builder() + .isLowRamDevice(deviceInfoCollect.isLowRamDevice) + .totalMem(deviceInfoCollect.totalMem) + .availableProcessors(deviceInfoCollect.availableProcessors) + .supportedAbi(deviceInfoCollect.supportedAbi) + .build() + return SyncRequest.Builder().hardwarePropertiesPayload(hardwarePropertiesPayload).build() + } + + @SuppressLint("DefaultLocale") + private fun createLocalePropertiesPayloadRequest(): SyncRequest { + val timeZone = TimeZone.getDefault() + val gmtFormat = String.format( + "GMT%+d:%02d", + timeZone.rawOffset / (60 * 60 * 1000), + abs(timeZone.rawOffset / (60 * 1000) % 60) + ) + val localePropertiesPayload = LocalePropertiesPayload.Builder() + .locale(gmtFormat) + .build() + return SyncRequest.Builder().localePropertiesPayload(localePropertiesPayload).build() + } + + private fun createPlayPartnerPropertiesPayloadRequest(): SyncRequest { + val playPartnerPropertiesPayload = PlayPartnerPropertiesPayload.Builder() + .marketId("am-google") + .partnerIdMs("play-ms-android-google") + .partnerIdAd("play-ad-ms-android-google") + .build() + return SyncRequest.Builder().playPartnerPropertiesPayload(playPartnerPropertiesPayload).build() + } + + private fun createPlayPropertiesPayload(context: Context): SyncRequest { + var version = 0 + try { + version = context.packageManager.getPackageInfo(Constants.VENDING_PACKAGE_NAME, 0).versionCode + } catch (exception: PackageManager.NameNotFoundException) { + Log.w(TAG, "[DAS] Could not find our package", exception) + } + val playPropertiesPayload = PlayPropertiesPayload.Builder().playVersion(version).build() + return SyncRequest.Builder().playPropertiesPayload(playPropertiesPayload).build() + } + + private fun createScreenPropertiesPayloadRequest(deviceInfoCollect: DeviceInfoCollect): SyncRequest { + val screenPropertiesPayload = ScreenPropertiesPayload.Builder() + .reqTouchScreen(deviceInfoCollect.reqTouchScreen) + .displayX(deviceInfoCollect.displayX) + .displayY(deviceInfoCollect.displayY) + .deviceStablePoint(deviceInfoCollect.deviceStablePoint) + .deviceStable(deviceInfoCollect.deviceStable) + .build() + return SyncRequest.Builder().screenPropertiesPayload(screenPropertiesPayload).build() + } + + private fun createSystemPropertiesPayloadRequest(deviceInfoCollect: DeviceInfoCollect): SyncRequest { + val systemPropertiesPayload = SystemPropertiesPayload.Builder() + .fingerprint(Build.FINGERPRINT) + .sdkInt(Build.VERSION.SDK_INT.toLong()) + .previewSdkFingerprint(deviceInfoCollect.previewSdkFingerprint) + .buildCodeName(deviceInfoCollect.buildCodeName) + .oemKey(deviceInfoCollect.oemKey) + .reqGlEsVersion(deviceInfoCollect.reqGlEsVersion) + .build() + return SyncRequest.Builder().systemPropertiesPayload(systemPropertiesPayload).build() + } + + private fun createHardwareIdentifierPayloadRequest(context: Context): SyncRequest? { + var hardwareIdentifierPayloadRequest: SyncRequest? = null + try { + val builder = HardwareIdentifierPayload.Builder() + val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + val randomIMEI = generateRandomIMEI() + var imeid: Long = if (TextUtils.isEmpty(randomIMEI) || !Pattern.compile("^[0-9]{15}$").matcher(randomIMEI) + .matches() + ) 0L else randomIMEI.toLong(10) or 0x1000000000000000L + if (imeid == 0L) { + var meid = "" + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + meid = telephonyManager.meid + } + if (!TextUtils.isEmpty(meid) && Pattern.compile("^[0-9a-fA-F]{14}$").matcher(meid).matches()) { + imeid = meid.toLong(16) or 0x1100000000000000L + if (imeid == 0L) { + if (context.packageManager.checkPermission( + "android.permission.READ_PRIVILEGED_PHONE_STATE", + Constants.VENDING_PACKAGE_NAME + ) == PackageManager.PERMISSION_GRANTED + ) { + var serial = "" + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + serial = Build.getSerial() + } + if (TextUtils.isEmpty(serial) && serial != "unknown") { + try { + val serialShaByte = MessageDigest.getInstance("SHA1").digest(serial.toByteArray()) + imeid = + ((serialShaByte[0].toLong()) and 0xFFL) shl 0x30 or 0x1400000000000000L or (((serialShaByte[1].toLong()) and 0xFFL) shl 40) or (((serialShaByte[2].toLong()) and 0xFFL) shl 0x20) or (((serialShaByte[3].toLong()) and 0xFFL) shl 24) or (((serialShaByte[4].toLong()) and 0xFFL) shl 16) or (((serialShaByte[5].toLong()) and 0xFFL) shl 8) or ((serialShaByte[6].toLong()) and 0xFFL) + } catch (noSuchAlgorithmException0: NoSuchAlgorithmException) { + Log.w(TAG, "No support for sha1?") + } + } + } + } + } + } + builder.imeId(imeid) + hardwareIdentifierPayloadRequest = SyncRequest.Builder().hardwareIdentifierPayload(builder.build()).build() + } catch (e: Exception) { + Log.w(TAG, "createHardwareIdentifierPayloadRequest error", e) + } + return hardwareIdentifierPayloadRequest + } + + private fun createEnterprisePropertiesPayloadRequest(context: Context): SyncRequest? { + var enterprisePropertiesPayloadRequest: SyncRequest? = null + try { + val enterprisePropertiesPayload = EnterprisePropertiesPayload.Builder() + val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager + val activeAdmins = devicePolicyManager.activeAdmins + if (activeAdmins != null) { + for (componentName in activeAdmins) { + val packageName = componentName.packageName + val packageInfo: PackageInfo? = context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES) + val isDeviceOwner = devicePolicyManager.isDeviceOwnerApp(packageName) + var isProfileOwner = false + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + isProfileOwner = devicePolicyManager.isProfileOwnerApp(packageName) + } + val policyType = + if (isDeviceOwner) MangedScope.MANAGED_DEVICES else if (isProfileOwner) MangedScope.MANAGED_PROFILES else MangedScope.LEGACY_DEVICE_ADMINS + val profileInfo = ProfileInfo.Builder() + .pkgName(componentName.packageName) + .policyType(policyType) + .pkgSHA1(calculateSHA(packageInfo!!.signatures[0].toByteArray(), "SHA1")) + .pkgSHA256(calculateSHA(packageInfo.signatures[0].toByteArray(), "SHA256")).build() + if (isProfileOwner) { + enterprisePropertiesPayload.profileOwner(profileInfo) + } + enterprisePropertiesPayload.default = enterprisePropertiesPayload.default.toMutableList() + .apply { add(profileInfo) } + } + } + enterprisePropertiesPayloadRequest = SyncRequest.Builder().enterprisePropertiesPayload(enterprisePropertiesPayload.build()).build() + } catch (e: Exception) { + Log.w(TAG, "createEnterprisePropertiesPayloadRequest error", e) + } + return enterprisePropertiesPayloadRequest + } + + private fun createDeviceInputPropertiesPayloadRequest(deviceInfoCollect: DeviceInfoCollect): SyncRequest { + val builder = DeviceInputPropertiesPayload.Builder() + .reqInputFeatures(deviceInfoCollect.reqInputFeaturesV1) + .reqKeyboardType(deviceInfoCollect.reqKeyboardType) + .reqNavigation(deviceInfoCollect.reqNavigation) + return SyncRequest.Builder().deviceInputPropertiesPayload(builder.build()).build() + } + + private fun createDeviceModelPayloadRequest(): SyncRequest { + val builder = DeviceModelPayload.Builder() + .manufacturer(Build.MANUFACTURER) + .model(Build.MODEL) + .device(Build.DEVICE) + .product(Build.PRODUCT) + .brand(Build.BRAND) + return SyncRequest.Builder().deviceModelPayload(builder.build()).build() + } + + private fun createDeviceCapabilitiesPayloadRequest(deviceInfoCollect: DeviceInfoCollect): SyncRequest { + val builder = DeviceCapabilitiesPayload.Builder() + builder.glExtensions(deviceInfoCollect.glExtensions) + val featureInfoList = builder.featureInfo.toMutableList() + for (featureInfoProto in deviceInfoCollect.featureInfoList) { + featureInfoList.add( + FeatureInfoProto.Builder() + .name(featureInfoProto.name) + .version(featureInfoProto.version) + .build() + ) + } + builder.featureInfo = featureInfoList + builder.systemSharedLibraryNames(deviceInfoCollect.systemSharedLibraryNames) + .locales(deviceInfoCollect.locales) + .unknownFlag(false) + return SyncRequest.Builder().deviceCapabilitiesPayload(builder.build()).build() + } + + private fun createDeviceAccountsPayloadRequest(context: Context, androidId: String): SyncRequest? { + var deviceAccountsPayloadRequest: SyncRequest? = null + try { + val accountManager = context.getSystemService(Context.ACCOUNT_SERVICE) as AccountManager + val accounts = accountManager.accounts + val builder = DeviceAccountsPayload.Builder() + val accountAssValues = builder.accountAss.toMutableList() + for (account in accounts) { + accountAssValues.add(AccountAssValue.Builder().value_(accountSha256(androidId, account)).build()) + } + builder.accountAss = accountAssValues + deviceAccountsPayloadRequest = SyncRequest.Builder().deviceAccountsPayload(builder.build()).build() + } catch (e: Exception) { + Log.w(TAG, "createDeviceAccountsPayloadRequest error", e) + } + return deviceAccountsPayloadRequest + } + + @SuppressLint("HardwareIds") + private fun createCarrierPropertiesPayloadRequest(context: Context): SyncRequest? { + var carrierPropertiesPayloadRequest: SyncRequest? = null + try { + val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + var simCardId = 0 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + simCardId = telephonyManager.simCarrierId + } + var carrierIdFromSimMccMnc = 0 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + carrierIdFromSimMccMnc = telephonyManager.carrierIdFromSimMccMnc + } + val telephonyInfo = TelephonyInfo.Builder().subscriberId(((telephonyManager.subscriberId.toLong() / 100000L).toString() + "00000").toLong()) + .operatorName(telephonyManager.simOperatorName).groupIdLevel(telephonyManager.groupIdLevel1).simCardId(simCardId) + .carrierIdFromSimMccMnc(carrierIdFromSimMccMnc).build() + val telephonyStateWrapper = TelephonyStateWrapper.Builder().telephonyInfo(telephonyInfo).build() + val carrierPropertiesPayload = + CarrierPropertiesPayload.Builder().telephonyStateValue(telephonyStateWrapper).simOperator(telephonyManager.simOperator).build() + carrierPropertiesPayloadRequest = SyncRequest.Builder().carrierPropertiesPayload(carrierPropertiesPayload).build() + } catch (securityException: SecurityException) { + Log.w(TAG, "SecurityException when reading IMSI.", securityException) + } catch (stateException: IllegalStateException) { + Log.w(TAG, "IllegalStateException when reading IMSI. This is a known SDK 31 Samsung bug.", stateException) + } catch (e: Exception) { + Log.w(TAG, "createCarrierPropertiesPayloadRequest error", e) + } + return carrierPropertiesPayloadRequest + } + + private fun accountSha256(androidId: String, account: Account): String? { + return try { + val androidIdAcc = (androidId + "-" + account.name).toByteArray() + val messageDigest0 = MessageDigest.getInstance("SHA256") + messageDigest0.update(androidIdAcc, 0, androidIdAcc.size) + Base64.encodeToString(messageDigest0.digest(), 11) + } catch (ignored: Exception) { + null + } + } + + private fun generateRandomIMEI(): String { + val random = Random() + val imeiBuilder = StringBuilder() + for (i in 0..13) { + val digit = random.nextInt(10) + imeiBuilder.append(digit) + } + val imei = imeiBuilder.toString() + val checkDigit = calculateCheckDigit(imei) + imeiBuilder.append(checkDigit) + return imeiBuilder.toString() + } + + private fun calculateCheckDigit(imei: String): Int { + var sum = 0 + for (i in imei.indices) { + var digit = Character.getNumericValue(imei[i]) + if (i % 2 == 1) { + digit *= 2 + } + if (digit > 9) { + digit -= 9 + } + sum += digit + } + return (10 - (sum % 10)) % 10 + } + + private fun calculateSHA(data: ByteArray, algorithm: String?): String? { + val messageDigest0: MessageDigest + try { + messageDigest0 = algorithm?.let { MessageDigest.getInstance(it) }!! + } catch (noSuchAlgorithmException0: NoSuchAlgorithmException) { + Log.w(TAG, "[DC] No support for %s?", noSuchAlgorithmException0) + return null + } + messageDigest0.update(data, 0, data.size) + return Base64.encodeToString(messageDigest0.digest(), 11) + } + + private fun fetchGLInfo(): ArrayList { + if (glInfoList.isNotEmpty()) return glInfoList + try { + val eGL100 = EGLContext.getEGL() as? EGL10 + val result = ArrayList() + val egl10Instance = eGL100?.let { EGL10Wrapper(it) } + val eglDisplay = eGL100!!.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY) + eGL100.eglInitialize(eglDisplay, IntArray(2)) + val ints = IntArray(1) + val configCount = if (eGL100.eglGetConfigs(eglDisplay, null, 0, ints)) ints[0] else 0 + val arrEglConfig = arrayOfNulls(configCount) + val eglConfigs = if (eGL100.eglGetConfigs(eglDisplay, arrEglConfig, configCount, IntArray(1))) arrEglConfig else null + val arrV1 = intArrayOf(0x3057, 1, 0x3056, 1, 0x3038) + for (v1 in 0 until configCount) { + if (egl10Instance?.eglGetConfigAttrib(eglDisplay, eglConfigs?.get(v1), 0x3027) != 0x3050 + && (egl10Instance?.eglGetConfigAttrib(eglDisplay, eglConfigs?.get(v1), 0x3033)?.and(1)) != 0 + ) { + val v2 = egl10Instance?.eglGetConfigAttrib(eglDisplay, eglConfigs?.get(v1), 0x3040) + if ((v2?.and(1)) != 0) { + egl10Instance?.let { wrapper -> buildGLStrings(wrapper, eglDisplay, eglConfigs?.get(v1), arrV1, null)?.let { result.add(it) } } + } + if ((v2?.and(4)) != 0) { + egl10Instance?.let { wrapper -> + buildGLStrings( + wrapper, + eglDisplay, + eglConfigs?.get(v1), + arrV1, + intArrayOf(0x3098, 2, 0x3038) + )?.let { result.add(it) } + } + } + } + } + egl10Instance?.instance?.eglTerminate(eglDisplay) + return result.also { glInfoList.addAll(it) } + } catch (e: Exception) { + Log.d(TAG, "fetchGLInfo: error", e) + } + return ArrayList() + } + + private fun buildGLStrings(egl10Tools: EGL10Wrapper, eglDisplay: EGLDisplay, eglConfig: EGLConfig?, arrV: IntArray, arrV1: IntArray?): FetchedGlStrings? { + val eglContext = egl10Tools.instance.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, arrV1) + if (eglContext != EGL10.EGL_NO_CONTEXT) { + val eglSurface = egl10Tools.instance.eglCreatePbufferSurface(eglDisplay, eglConfig, arrV) + if (eglSurface == EGL10.EGL_NO_SURFACE) { + egl10Tools.eglDestroyContext(eglDisplay, eglContext) + return null + } + egl10Tools.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) + val result = FetchedGlStrings(0, null, null, null, null) + val glExtensions = GLES10.glGetString(GLES10.GL_EXTENSIONS) + if (!TextUtils.isEmpty(glExtensions)) { + result.glExtensions = glExtensions.split(" ".toRegex()).dropLastWhile { it.isEmpty() } + } + result.glRenderer = GLES10.glGetString(GLES10.GL_RENDERER) + result.glVendor = GLES10.glGetString(GLES10.GL_VENDOR) + result.glVersion = GLES10.glGetString(GLES10.GL_VERSION) + if (result.glExtensions != null) { + egl10Tools.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT) + egl10Tools.instance.eglDestroySurface(eglDisplay, eglSurface) + egl10Tools.eglDestroyContext(eglDisplay, eglContext) + return result + } + throw IllegalStateException("Missing required properties ") + } + return null + } + + private fun getSystemProperty(key: String?, defaultValue: String?): String? { + var value = defaultValue + try { + @SuppressLint("PrivateApi") val systemPropertiesClass = Class.forName("android.os.SystemProperties") + val getMethod = systemPropertiesClass.getMethod("get", String::class.java, String::class.java) + value = getMethod.invoke(null, key, defaultValue) as String + } catch (e: Exception) { + Log.w(TAG, "Unable to retrieve system property", e) + } + return value + } + + private fun calculatePoint(point: Point, v: Int): Int { + val f = point.x.toFloat() + val v1 = ((point.y.toFloat()) * (160.0f / (v.toFloat()))).toInt() + if (v1 < 470) { + return 17 + } + val v2 = (f * (160.0f / (v.toFloat()))).toInt() + if (v1 >= 960 && v2 >= 720) { + return if (v1 * 3 / 5 < v2 - 1) 20 else 4 + } + val v3 = if (v1 < 640 || v2 < 480) 2 else 3 + return if (v1 * 3 / 5 < v2 - 1) v3 or 16 else v3 + } + + internal class EGL10Wrapper(val instance: EGL10) { + fun eglGetConfigAttrib(eglDisplay: EGLDisplay?, eglConfig: EGLConfig?, v: Int): Int { + val value = IntArray(1) + instance.eglGetConfigAttrib(eglDisplay, eglConfig, v, value) + return value[0] + } + + fun eglDestroyContext(eglDisplay: EGLDisplay?, eglContext: EGLContext?) { + instance.eglDestroyContext(eglDisplay, eglContext) + } + + fun eglMakeCurrent(eglDisplay: EGLDisplay?, draw: EGLSurface?, read: EGLSurface?, eglContext: EGLContext?) { + instance.eglMakeCurrent(eglDisplay, draw, read, eglContext) + } + } + + internal class FetchedGlStrings( + var contextClientVersion: Int, + var glExtensions: List?, + var glRenderer: String?, + var glVendor: String?, + var glVersion: String? + ) +} \ No newline at end of file diff --git a/vending-app/src/main/kotlin/com/google/android/finsky/accounts/impl/AccountsChangedReceiver.kt b/vending-app/src/main/kotlin/com/google/android/finsky/accounts/impl/AccountsChangedReceiver.kt new file mode 100644 index 0000000000..529ac7af6f --- /dev/null +++ b/vending-app/src/main/kotlin/com/google/android/finsky/accounts/impl/AccountsChangedReceiver.kt @@ -0,0 +1,62 @@ +/** + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.finsky.accounts.impl + +import android.accounts.AccountManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log +import com.android.vending.licensing.AUTH_TOKEN_SCOPE +import com.android.vending.licensing.getAuthToken +import com.android.vending.licensing.getLicenseRequestHeaders +import com.google.android.finsky.SyncResponse +import com.google.android.finsky.DeviceSyncInfo +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.microg.gms.auth.AuthConstants +import org.microg.gms.profile.ProfileManager +import org.microg.vending.billing.GServices +import org.microg.vending.billing.core.HttpClient +import java.lang.RuntimeException + +private const val TAG = "AccountsChangedReceiver" + +class AccountsChangedReceiver : BroadcastReceiver() { + + @OptIn(DelicateCoroutinesApi::class) + override fun onReceive(context: Context, intent: Intent?) { + Log.d(TAG, "onReceive: intent-> $intent") + var accountName: String? = null + if (intent?.let { accountName = it.getStringExtra(AccountManager.KEY_ACCOUNT_NAME) } == null) { + return + } + GlobalScope.launch(Dispatchers.IO) { + runCatching { + val account = AccountManager.get(context).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE).firstOrNull { + it.name == accountName + } ?: throw RuntimeException("account is null") + val oauthToken = account.let { + AccountManager.get(context).getAuthToken(it, AUTH_TOKEN_SCOPE, false).getString(AccountManager.KEY_AUTHTOKEN) + } ?: throw RuntimeException("oauthToken is null") + ProfileManager.ensureInitialized(context) + val androidId = GServices.getString(context.contentResolver, "android_id", "0")?.toLong() ?: 1 + HttpClient(context).post( + url = "https://play-fe.googleapis.com/fdfe/sync", + headers = getLicenseRequestHeaders(oauthToken, androidId), + payload = DeviceSyncInfo.buildSyncRequest(context, androidId.toString(), account), + adapter = SyncResponse.ADAPTER + ) + Log.d(TAG, "onReceive: sync success") + }.onFailure { + Log.d(TAG, "onReceive: sync error", it) + } + } + } + +} \ No newline at end of file diff --git a/vending-app/src/main/proto/SyncRequest.proto b/vending-app/src/main/proto/SyncRequest.proto new file mode 100644 index 0000000000..546b390380 --- /dev/null +++ b/vending-app/src/main/proto/SyncRequest.proto @@ -0,0 +1,188 @@ +option java_package = "com.google.android.finsky"; +option java_multiple_files = true; + +message SyncReqWrapper { + repeated SyncRequest request = 1; +} + +message SyncRequest { + oneof payload { + AccountAssociationPayload accountAssociationPayload = 7; + DeviceAccountsPayload deviceAccountsPayload = 8; + CarrierPropertiesPayload carrierPropertiesPayload = 9; + DeviceCapabilitiesPayload deviceCapabilitiesPayload = 10; + DeviceInputPropertiesPayload deviceInputPropertiesPayload = 11; + DeviceModelPayload deviceModelPayload = 12; + EnterprisePropertiesPayload enterprisePropertiesPayload = 13; + HardwareIdentifierPayload hardwareIdentifierPayload = 14; + HardwarePropertiesPayload hardwarePropertiesPayload = 15; + LocalePropertiesPayload localePropertiesPayload = 16; + NotificationRoutingInfoPayload notificationRoutingInfoPayload = 17; + PlayPartnerPropertiesPayload playPartnerPropertiesPayload = 18; + PlayPropertiesPayload playPropertiesPayload = 19; + ScreenPropertiesPayload screenPropertiesPayload = 20; + SystemPropertiesPayload systemPropertiesPayload = 21; + GpuPayload gpuPayload = 24; + } +} + +message AccountAssociationPayload { + optional AccountAssValue accountAss = 1; +} + +message AccountAssValue { + optional string value = 1; +} + +message DeviceAccountsPayload { + repeated AccountAssValue accountAss = 1; +} + +message CarrierPropertiesPayload { + optional string simOperator = 1; + optional TelephonyStateWrapper telephonyStateValue = 2; +} + +message TelephonyStateWrapper { + optional TelephonyInfo telephonyInfo = 1; +} + +message TelephonyInfo { + optional int64 subscriberId = 1; + optional string operatorName = 2; + optional string groupIdLevel = 3; + optional int32 simCardId = 6; + optional int32 carrierIdFromSimMccMnc = 7; +} + +message DeviceCapabilitiesPayload { + repeated FeatureInfoProto featureInfo = 1; + repeated string systemSharedLibraryNames = 2; + repeated string locales = 3; + repeated string glExtensions = 4; + optional bool unknownFlag = 5; +} + +message DeviceInputPropertiesPayload { + optional int32 reqKeyboardType = 1; + optional bool reqInputFeatures = 2; + optional int32 reqNavigation = 3; +} + +message DeviceModelPayload { + optional string manufacturer = 1; + optional string model = 2; + optional string device = 3; + optional string product = 4; + optional string brand = 5; +} + +message EnterprisePropertiesPayload { + optional ProfileInfo profileOwner = 1; + repeated ProfileInfo default = 2; +} + +message ProfileInfo { + optional string pkgName = 1; + optional string pkgSHA1 = 2; + optional string pkgSHA256 = 3; + optional MangedScope policyType = 4; +} + +enum MangedScope { + UNKNOWN_MANAGED_SCOPE = 0; + MANAGED_DEVICES = 1; + MANAGED_PROFILES = 2; + MANAGED_AVENGER = 3; + LEGACY_DEVICE_ADMINS = 4; +} + +message HardwareIdentifierPayload { + optional fixed64 imeId = 1; +} + +message HardwarePropertiesPayload { + optional bool isLowRamDevice = 1; + optional int64 totalMem = 2; + optional int32 availableProcessors = 3; + repeated string supportedAbi = 4; +} + +message LocalePropertiesPayload { + optional string locale = 1; +} + +message NotificationRoutingInfoPayload { + optional string info = 1; +} + +message PlayPartnerPropertiesPayload { + optional string marketId = 1; + optional string partnerIdMs = 2; + optional string partnerIdAd = 3; +} + +message PlayPropertiesPayload { + optional int32 playVersion = 2; +} + +message ScreenPropertiesPayload { + optional int32 reqTouchScreen = 1; + optional int32 displayX = 2; + optional int32 displayY = 3; + optional int32 deviceStablePoint = 4; + optional int32 deviceStable = 5; +} + +message SystemPropertiesPayload { + optional string fingerprint = 1; + optional int64 sdkInt = 2; + optional string previewSdkFingerprint = 3; + optional string buildCodeName = 4; + optional string oemKey = 5; + optional int32 reqGlEsVersion = 6; +} + +message GpuPayload { + optional GpuInfoWrapper gpuInfo = 1; +} + +message GpuInfoWrapper { + optional string glRenderer = 1; + optional string glVendor = 2; + optional string glVersion = 3; +} + +message DeviceInfoCollect { + optional int32 reqTouchScreen = 1; + optional int32 reqKeyboardType = 2; + optional int32 reqNavigation = 3; + optional int32 deviceStablePoint = 4; + optional bool reqInputFeaturesV1 = 5; + optional bool reqInputFeaturesV2 = 6; + optional int32 deviceStable = 7; + optional int32 reqGlEsVersion = 8; + repeated string systemSharedLibraryNames = 9; + repeated string featureNames = 10; + repeated string supportedAbi = 11; + optional int32 displayX = 12; + optional int32 displayY = 13; + repeated string locales = 14; + repeated string glExtensions = 15; + optional int32 smallestScreenWidthDp = 18; + optional bool isLowRamDevice = 19; + optional int64 totalMem = 20; + optional int32 availableProcessors = 21; + repeated FeatureInfoProto featureInfoList = 26; + optional int32 screenLayout = 27; + optional string oemKey = 29; + optional string buildCodeName = 30; + optional string previewSdkFingerprint = 31; +} + +message FeatureInfoProto { + optional string name = 1; + optional int32 version = 2; +} + +message SyncResponse {}