diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
index 4b5575bb3b..e752bfea67 100644
--- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
+++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
@@ -199,12 +199,14 @@ object SettingsContract {
const val LICENSING_PURCHASE_FREE_APPS = "vending_licensing_purchase_free_apps"
const val BILLING = "vending_billing"
const val ASSET_DELIVERY = "vending_asset_delivery"
+ const val ASSET_DEVICE_SYNC = "vending_device_sync"
val PROJECTION = arrayOf(
LICENSING,
LICENSING_PURCHASE_FREE_APPS,
BILLING,
ASSET_DELIVERY,
+ ASSET_DEVICE_SYNC,
)
}
diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
index 796f6feb85..96dab1b602 100644
--- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
+++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
@@ -356,6 +356,7 @@ class SettingsProvider : ContentProvider() {
Vending.LICENSING_PURCHASE_FREE_APPS -> getSettingsBoolean(key, false)
Vending.BILLING -> getSettingsBoolean(key, false)
Vending.ASSET_DELIVERY -> getSettingsBoolean(key, false)
+ Vending.ASSET_DEVICE_SYNC -> getSettingsBoolean(key, false)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
@@ -369,6 +370,7 @@ class SettingsProvider : ContentProvider() {
Vending.LICENSING_PURCHASE_FREE_APPS -> editor.putBoolean(key, value as Boolean)
Vending.BILLING -> editor.putBoolean(key, value as Boolean)
Vending.ASSET_DELIVERY -> editor.putBoolean(key, value as Boolean)
+ Vending.ASSET_DEVICE_SYNC -> editor.putBoolean(key, value as Boolean)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
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/huawei/AndroidManifest.xml b/play-services-core/src/huawei/AndroidManifest.xml
index a6ca2b9e94..e39829b8a2 100644
--- a/play-services-core/src/huawei/AndroidManifest.xml
+++ b/play-services-core/src/huawei/AndroidManifest.xml
@@ -40,5 +40,8 @@
+
\ No newline at end of file
diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml
index 0dbd7c07e2..c2a67c5d7e 100644
--- a/play-services-core/src/main/AndroidManifest.xml
+++ b/play-services-core/src/main/AndroidManifest.xml
@@ -127,6 +127,11 @@
android:permissionGroup="com.google.android.gms.permission.CAR_INFORMATION"
android:protectionLevel="dangerous" />
+
+
+
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 831c4751f4..c0822cd618 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,8 @@ 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 static final String PERMISSION_UPDATE_ACCOUNT = "com.google.android.gms.auth.permission.GOOGLE_ACCOUNT_CHANGE";
private final FidoHandler fidoHandler = new FidoHandler(this);
private final DroidGuardHandler dgHandler = new DroidGuardHandler(this);
@@ -358,6 +362,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, PERMISSION_UPDATE_ACCOUNT);
}
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/play-services-core/src/main/kotlin/org/microg/gms/ui/VendingFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/VendingFragment.kt
index d20702e79f..6f81909840 100644
--- a/play-services-core/src/main/kotlin/org/microg/gms/ui/VendingFragment.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/VendingFragment.kt
@@ -19,6 +19,7 @@ class VendingFragment : PreferenceFragmentCompat() {
private lateinit var licensingPurchaseFreeAppsEnabled: TwoStatePreference
private lateinit var iapEnable: TwoStatePreference
private lateinit var assetDeliveryEnabled: TwoStatePreference
+ private lateinit var deviceSyncEnabled: TwoStatePreference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_vending)
@@ -73,6 +74,18 @@ class VendingFragment : PreferenceFragmentCompat() {
}
true
}
+
+ deviceSyncEnabled = preferenceScreen.findPreference(PREF_DEVICE_SYNC_ENABLED) ?: deviceSyncEnabled
+ deviceSyncEnabled.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
+ val appContext = requireContext().applicationContext
+ lifecycleScope.launchWhenResumed {
+ if (newValue is Boolean) {
+ VendingPreferences.setDeviceSyncEnabled(appContext, newValue)
+ }
+ updateContent()
+ }
+ true
+ }
}
override fun onResume() {
@@ -87,6 +100,7 @@ class VendingFragment : PreferenceFragmentCompat() {
licensingPurchaseFreeAppsEnabled.isChecked = VendingPreferences.isLicensingPurchaseFreeAppsEnabled(appContext)
iapEnable.isChecked = VendingPreferences.isBillingEnabled(appContext)
assetDeliveryEnabled.isChecked = VendingPreferences.isAssetDeliveryEnabled(appContext)
+ deviceSyncEnabled.isChecked = VendingPreferences.isDeviceSyncEnabled(appContext)
}
}
@@ -95,5 +109,6 @@ class VendingFragment : PreferenceFragmentCompat() {
const val PREF_LICENSING_PURCHASE_FREE_APPS_ENABLED = "vending_licensing_purchase_free_apps"
const val PREF_IAP_ENABLED = "vending_iap"
const val PREF_ASSET_DELIVERY_ENABLED = "vending_asset_delivery"
+ const val PREF_DEVICE_SYNC_ENABLED = "vending_device_sync"
}
}
\ No newline at end of file
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/vending/VendingPreferences.kt b/play-services-core/src/main/kotlin/org/microg/gms/vending/VendingPreferences.kt
index ec9e1a42dc..6cf38827d2 100644
--- a/play-services-core/src/main/kotlin/org/microg/gms/vending/VendingPreferences.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/vending/VendingPreferences.kt
@@ -69,4 +69,19 @@ object VendingPreferences {
put(SettingsContract.Vending.ASSET_DELIVERY, enabled)
}
}
+
+ @JvmStatic
+ fun isDeviceSyncEnabled(context: Context): Boolean {
+ val projection = arrayOf(SettingsContract.Vending.ASSET_DEVICE_SYNC)
+ return SettingsContract.getSettings(context, SettingsContract.Vending.getContentUri(context), projection) { c ->
+ c.getInt(0) != 0
+ }
+ }
+
+ @JvmStatic
+ fun setDeviceSyncEnabled(context: Context, enabled: Boolean) {
+ SettingsContract.setSettings(context, SettingsContract.Vending.getContentUri(context)) {
+ put(SettingsContract.Vending.ASSET_DEVICE_SYNC, enabled)
+ }
+ }
}
\ No newline at end of file
diff --git a/play-services-core/src/main/res/values-zh-rCN/strings.xml b/play-services-core/src/main/res/values-zh-rCN/strings.xml
index c9607f5c37..036eb303d8 100644
--- a/play-services-core/src/main/res/values-zh-rCN/strings.xml
+++ b/play-services-core/src/main/res/values-zh-rCN/strings.xml
@@ -264,4 +264,6 @@ microG GmsCore 内置一套自由的 SafetyNet 实现,但是官方服务器要
当使用 Play 资产传递的应用请求时下载额外的资产
Google Play 资产传递
启用按需资产传递
+ 使用 Play 资产传递的应用请求时将会根据当前使用设备信息下载更多的资产
+ 启用设备信息同步
\ No newline at end of file
diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml
index 6fd512c5eb..8397fc68a0 100644
--- a/play-services-core/src/main/res/values/strings.xml
+++ b/play-services-core/src/main/res/values/strings.xml
@@ -297,6 +297,8 @@ Please set up a password, PIN, or pattern lock screen."
Google Play Asset Delivery
Enable on-demand asset delivery
Download additional assets when requested by apps that use Play Asset Delivery
+ Applications using Play Asset Delivery will download additional assets based on the information of the device currently in use.
+ Enable device information sync
Cancel
Continue
diff --git a/play-services-core/src/main/res/xml/preferences_vending.xml b/play-services-core/src/main/res/xml/preferences_vending.xml
index 509e994b83..efd8b4c3b2 100644
--- a/play-services-core/src/main/res/xml/preferences_vending.xml
+++ b/play-services-core/src/main/res/xml/preferences_vending.xml
@@ -61,5 +61,11 @@
android:key="vending_asset_delivery"
android:persistent="false"
app:iconSpaceReserved="false" />
+
diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml
index eb12ae7378..a8b462ccca 100644
--- a/vending-app/src/main/AndroidManifest.xml
+++ b/vending-app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
+
+
+
+
+
+
+
diff --git a/vending-app/src/main/java/com/android/vending/VendingPreferences.kt b/vending-app/src/main/java/com/android/vending/VendingPreferences.kt
index ddd17e07b6..4d23d3e703 100644
--- a/vending-app/src/main/java/com/android/vending/VendingPreferences.kt
+++ b/vending-app/src/main/java/com/android/vending/VendingPreferences.kt
@@ -41,4 +41,12 @@ object VendingPreferences {
c.getInt(0) != 0
}
}
+
+ @JvmStatic
+ fun isDeviceSyncEnabled(context: Context): Boolean {
+ val projection = arrayOf(SettingsContract.Vending.ASSET_DEVICE_SYNC)
+ return SettingsContract.getSettings(context, SettingsContract.Vending.getContentUri(context), projection) { c ->
+ c.getInt(0) != 0
+ }
+ }
}
\ No newline at end of file
diff --git a/vending-app/src/main/java/org/microg/vending/billing/core/GooglePlayApi.kt b/vending-app/src/main/java/org/microg/vending/billing/core/GooglePlayApi.kt
index 9cab4f4f7e..a8e539e34e 100644
--- a/vending-app/src/main/java/org/microg/vending/billing/core/GooglePlayApi.kt
+++ b/vending-app/src/main/java/org/microg/vending/billing/core/GooglePlayApi.kt
@@ -12,5 +12,6 @@ class GooglePlayApi {
const val URL_AUTH_PROOF_TOKENS = "https://www.googleapis.com/reauth/v1beta/users/me/reauthProofTokens"
const val URL_DETAILS = "$URL_FDFE/details"
const val URL_PURCHASE = "$URL_FDFE/purchase"
+ const val URL_SYNC = "$URL_FDFE/sync"
}
}
\ No newline at end of file
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..e535a307b6
--- /dev/null
+++ b/vending-app/src/main/kotlin/com/google/android/finsky/DeviceSyncInfo.kt
@@ -0,0 +1,584 @@
+/**
+ * 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.telephony.TelephonyManager
+import android.text.TextUtils
+import android.util.Base64
+import android.util.DisplayMetrics
+import android.util.Log
+import android.os.Build.VERSION.SDK_INT
+import android.view.WindowManager
+import org.microg.gms.common.Constants
+import org.microg.gms.profile.Build
+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: Long, 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: Long, 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, androidId)
+ 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 (SDK_INT >= 24) {
+ 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 (SDK_INT >= 24) {
+ 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 (SDK_INT >= 24) {
+ 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 (SDK_INT >= 21) {
+ 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 (SDK_INT >= 24) {
+ 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 randomIMEI = generateRandomIMEI()
+ val imeId: Long = if (TextUtils.isEmpty(randomIMEI) || !Pattern.compile("^[0-9]{15}$").matcher(randomIMEI).matches())
+ 0L else randomIMEI.toLong(10) or 0x1000000000000000L
+ 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 (SDK_INT >= 21) {
+ 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: Long): 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, androidId: Long): SyncRequest? {
+ var carrierPropertiesPayloadRequest: SyncRequest? = null
+ try {
+ val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
+ var simCardId = 0
+ if (SDK_INT >= 28) {
+ simCardId = telephonyManager.simCarrierId
+ }
+ var carrierIdFromSimMccMnc = 0
+ if (SDK_INT >= 29) {
+ carrierIdFromSimMccMnc = telephonyManager.carrierIdFromSimMccMnc
+ }
+ val telephonyInfo = TelephonyInfo.Builder()
+ .subscriberId(androidId)
+ .operatorName(telephonyManager.simOperatorName)
+ .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: Long, account: Account): String? {
+ return try {
+ val androidIdAcc = (androidId.toString() + "-" + 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..9098f2532f
--- /dev/null
+++ b/vending-app/src/main/kotlin/com/google/android/finsky/accounts/impl/AccountsChangedReceiver.kt
@@ -0,0 +1,70 @@
+/**
+ * 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.VendingPreferences
+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.GooglePlayApi
+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")
+ val deviceSyncEnabled = VendingPreferences.isDeviceSyncEnabled(context)
+ if (!deviceSyncEnabled) {
+ Log.d(TAG, "onReceive: deviceSyncEnabled is false")
+ return
+ }
+ var accountName: String? = null
+ if (intent?.let { accountName = it.getStringExtra(AccountManager.KEY_ACCOUNT_NAME) } == null) {
+ Log.d(TAG, "onReceive: accountName is empty")
+ 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 = GooglePlayApi.URL_SYNC,
+ headers = getLicenseRequestHeaders(oauthToken, androidId),
+ payload = DeviceSyncInfo.buildSyncRequest(context, androidId, 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 {}