Add profile manager

This commit is contained in:
Marvin W 2021-11-24 23:39:33 -06:00
parent 6d45bfb7ed
commit a7b2b7e3f8
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
18 changed files with 488 additions and 140 deletions

View File

@ -1,62 +0,0 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.common;
import android.annotation.TargetApi;
import java.util.Locale;
import java.util.Random;
// TODO: Make flexible
public class Build {
public String board = android.os.Build.BOARD;
public String bootloader = android.os.Build.BOOTLOADER;
public String brand = android.os.Build.BRAND;
public String cpu_abi = android.os.Build.CPU_ABI;
public String cpu_abi2 = android.os.Build.CPU_ABI2;
@TargetApi(21)
public String[] supported_abis = android.os.Build.VERSION.SDK_INT >= 21 ? android.os.Build.SUPPORTED_ABIS : new String[0];
public String device = android.os.Build.DEVICE;
public String display = android.os.Build.DISPLAY;
public String fingerprint = android.os.Build.FINGERPRINT;
public String hardware = android.os.Build.HARDWARE;
public String host = android.os.Build.HOST;
public String id = android.os.Build.ID;
public String manufacturer = android.os.Build.MANUFACTURER;
public String model = android.os.Build.MODEL;
public String product = android.os.Build.PRODUCT;
public String radio = android.os.Build.RADIO;
public String serial = generateSerialNumber(); // TODO: static
public String tags = android.os.Build.TAGS;
public long time = android.os.Build.TIME;
public String type = android.os.Build.TYPE;
public String user = android.os.Build.USER;
public String version_codename = android.os.Build.VERSION.CODENAME;
public String version_incremental = android.os.Build.VERSION.INCREMENTAL;
public String version_release = android.os.Build.VERSION.RELEASE;
public String version_sdk = android.os.Build.VERSION.SDK;
public int version_sdk_int = android.os.Build.VERSION.SDK_INT;
private String generateSerialNumber() {
String serial = "008741";
Random rand = new Random();
for (int i = 0; i < 10; i++)
serial += Integer.toString(rand.nextInt(16), 16);
serial = serial.toUpperCase(Locale.US);
return serial;
}
}

View File

@ -33,10 +33,6 @@ public class Utils {
return Locale.getDefault(); // TODO return Locale.getDefault(); // TODO
} }
public static Build getBuild(Context context) {
return new Build();
}
public static DeviceIdentifier getDeviceIdentifier(Context context) { public static DeviceIdentifier getDeviceIdentifier(Context context) {
return new DeviceIdentifier(); return new DeviceIdentifier();
} }

View File

@ -0,0 +1,94 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.profile
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import kotlin.random.Random
object Build {
@JvmField
var BOARD: String? = null
@JvmField
var BOOTLOADER: String? = null
@JvmField
var BRAND: String? = null
@JvmField
var CPU_ABI: String? = null
@JvmField
var CPU_ABI2: String? = null
@JvmField
@TargetApi(21)
var SUPPORTED_ABIS: Array<String> = emptyArray()
@JvmField
var DEVICE: String? = null
@JvmField
var DISPLAY: String? = null
@JvmField
var FINGERPRINT: String? = null
@JvmField
var HARDWARE: String? = null
@JvmField
var HOST: String? = null
@JvmField
var ID: String? = null
@JvmField
var MANUFACTURER: String? = null
@JvmField
var MODEL: String? = null
@JvmField
var PRODUCT: String? = null
@JvmField
var RADIO: String? = null
@JvmField
var SERIAL: String? = null
@JvmField
var TAGS: String? = null
@JvmField
var TIME: Long = 0L
@JvmField
var TYPE: String? = null
@JvmField
var USER: String? = null
object VERSION {
@JvmField
var CODENAME: String? = null
@JvmField
var INCREMENTAL: String? = null
@JvmField
var RELEASE: String? = null
@JvmField
var SDK: String? = null
@JvmField
var SDK_INT: Int = 0
}
}

View File

@ -0,0 +1,231 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.profile
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.util.Log
import org.microg.gms.settings.SettingsContract
import org.microg.gms.settings.SettingsContract.Profile
import org.xmlpull.v1.XmlPullParser
import kotlin.random.Random
object ProfileManager {
private const val TAG = "ProfileManager"
const val PROFILE_REAL = "real"
const val PROFILE_AUTO = "auto"
const val PROFILE_NATIVE = "native"
private var initialized = false
private fun getProfileFromSettings(context: Context) = SettingsContract.getSettings(context, Profile.getContentUri(context), arrayOf(Profile.PROFILE)) { it.getString(0) }
private fun getAutoProfile(context: Context): String {
val profile = "${android.os.Build.DEVICE}_${android.os.Build.VERSION.SDK_INT}"
if (hasProfile(context, profile)) return profile
return PROFILE_NATIVE
}
private fun getProfileResId(context: Context, profile: String) = context.resources.getIdentifier("${context.packageName}:xml/profile_$profile", null, null)
private fun hasProfile(context: Context, profile: String): Boolean = getProfileResId(context, profile) != 0
private fun getProfileData(context: Context, profile: String, realData: Map<String, String>): Map<String, String>? {
try {
if (profile in listOf(PROFILE_REAL, PROFILE_NATIVE)) return null
val profileResId = getProfileResId(context, profile)
if (profileResId == 0) return null
val resultData = mutableMapOf<String, String>()
resultData.putAll(realData)
context.resources.getXml(profileResId).use {
var next = it.next()
while (next != XmlPullParser.END_DOCUMENT) {
when (next) {
XmlPullParser.START_TAG -> when (it.name) {
"data" -> {
val key = it.getAttributeValue(null, "key")
val value = it.getAttributeValue(null, "value")
resultData[key] = value
Log.d(TAG, "Overwrite from profile: $key = $value")
}
}
}
next = it.next()
}
}
for (entry in resultData) {
Log.d(TAG, "<data key=\"${entry.key}\" value=\"${entry.value}\" />")
}
return resultData
} catch (e: Exception) {
Log.w(TAG, e)
return null
}
}
private fun getActiveProfile(context: Context) = getProfileFromSettings(context).let { if (it != PROFILE_AUTO) it else getAutoProfile(context) }
private fun getSerialFromSettings(context: Context): String? = SettingsContract.getSettings(context, Profile.getContentUri(context), arrayOf(Profile.SERIAL)) { it.getString(0) }
private fun saveSerial(context: Context, serial: String) = SettingsContract.setSettings(context, Profile.getContentUri(context)) { put(Profile.SERIAL, serial) }
private fun randomSerial(template: String, prefixLength: Int = (template.length / 2).coerceAtMost(6)): String {
val serial = StringBuilder()
template.forEachIndexed { index, c ->
serial.append(when {
index < prefixLength -> c
c.isDigit() -> '0' + Random.nextInt(10)
c.isLowerCase() && c <= 'f' -> 'a' + Random.nextInt(6)
c.isLowerCase() -> 'a' + Random.nextInt(26)
c.isUpperCase() && c <= 'F' -> 'A' + Random.nextInt(6)
c.isUpperCase() -> 'A' + Random.nextInt(26)
else -> c
})
}
return serial.toString()
}
@SuppressLint("MissingPermission")
private fun getProfileSerialTemplate(context: Context, profile: String): String {
// Native
if (profile in listOf(PROFILE_REAL, PROFILE_NATIVE)) {
return kotlin.runCatching {
if (android.os.Build.VERSION.SDK_INT >= 26) {
android.os.Build.getSerial()
} else {
null
}
}.getOrNull()?.takeIf { it != android.os.Build.UNKNOWN } ?: android.os.Build.SERIAL
}
// From profile
try {
val profileResId = getProfileResId(context, profile)
if (profileResId != 0) {
context.resources.getXml(profileResId).use {
var next = it.next()
while (next != XmlPullParser.END_DOCUMENT) {
when (next) {
XmlPullParser.START_TAG -> when (it.name) {
"serial" -> return it.getAttributeValue(null, "template")
}
}
next = it.next()
}
}
}
} catch (e: Exception) {
Log.w(TAG, e)
}
// Fallback
return "008741A0B2C4D6E8"
}
@SuppressLint("MissingPermission")
private fun getEffectiveProfileSerial(context: Context, profile: String): String {
getSerialFromSettings(context)?.let { return it }
val serialTemplate = getProfileSerialTemplate(context, profile)
val serial = when {
profile == PROFILE_REAL && serialTemplate != android.os.Build.UNKNOWN -> serialTemplate
else -> randomSerial(serialTemplate)
}
saveSerial(context, serial)
return serial
}
private fun getRealData(): Map<String, String> = mutableMapOf(
"Build.BOARD" to android.os.Build.BOARD,
"Build.BOOTLOADER" to android.os.Build.BOOTLOADER,
"Build.BRAND" to android.os.Build.BRAND,
"Build.CPU_ABI" to android.os.Build.CPU_ABI,
"Build.CPU_ABI2" to android.os.Build.CPU_ABI2,
"Build.DEVICE" to android.os.Build.DEVICE,
"Build.DISPLAY" to android.os.Build.DISPLAY,
"Build.FINGERPRINT" to android.os.Build.FINGERPRINT,
"Build.HARDWARE" to android.os.Build.HARDWARE,
"Build.HOST" to android.os.Build.HOST,
"Build.ID" to android.os.Build.ID,
"Build.MANUFACTURER" to android.os.Build.MANUFACTURER,
"Build.MODEL" to android.os.Build.MODEL,
"Build.PRODUCT" to android.os.Build.PRODUCT,
"Build.RADIO" to android.os.Build.RADIO,
"Build.SERIAL" to android.os.Build.SERIAL,
"Build.TAGS" to android.os.Build.TAGS,
"Build.TIME" to android.os.Build.TIME.toString(),
"Build.TYPE" to android.os.Build.TYPE,
"Build.USER" to android.os.Build.USER,
"Build.VERSION.CODENAME" to android.os.Build.VERSION.CODENAME,
"Build.VERSION.INCREMENTAL" to android.os.Build.VERSION.INCREMENTAL,
"Build.VERSION.RELEASE" to android.os.Build.VERSION.RELEASE,
"Build.VERSION.SDK" to android.os.Build.VERSION.SDK,
"Build.VERSION.SDK_INT" to android.os.Build.VERSION.SDK_INT.toString()
).apply {
if (android.os.Build.VERSION.SDK_INT > 21) {
put("Build.SUPPORTED_ABIS", android.os.Build.SUPPORTED_ABIS.joinToString(","))
}
}
private fun applyProfileData(profileData: Map<String, String>) {
fun applyStringField(key: String, valueSetter: (String) -> Unit) = profileData[key]?.let { valueSetter(it) }
fun applyIntField(key: String, valueSetter: (Int) -> Unit) = profileData[key]?.toIntOrNull()?.let { valueSetter(it) }
fun applyLongField(key: String, valueSetter: (Long) -> Unit) = profileData[key]?.toLongOrNull()?.let { valueSetter(it) }
applyStringField("Build.BOARD") { Build.BOARD = it }
applyStringField("Build.BOOTLOADER") { Build.BOOTLOADER = it }
applyStringField("Build.BRAND") { Build.BRAND = it }
applyStringField("Build.CPU_ABI") { Build.CPU_ABI = it }
applyStringField("Build.CPU_ABI2") { Build.CPU_ABI2 = it }
applyStringField("Build.DEVICE") { Build.DEVICE = it }
applyStringField("Build.DISPLAY") { Build.DISPLAY = it }
applyStringField("Build.FINGERPRINT") { Build.FINGERPRINT = it }
applyStringField("Build.HARDWARE") { Build.HARDWARE = it }
applyStringField("Build.HOST") { Build.HOST = it }
applyStringField("Build.ID") { Build.ID = it }
applyStringField("Build.MANUFACTURER") { Build.MANUFACTURER = it }
applyStringField("Build.MODEL") { Build.MODEL = it }
applyStringField("Build.PRODUCT") { Build.PRODUCT = it }
applyStringField("Build.RADIO") { Build.RADIO = it }
applyStringField("Build.SERIAL") { Build.SERIAL = it }
applyStringField("Build.TAGS") { Build.TAGS = it }
applyLongField("Build.TIME") { Build.TIME = it }
applyStringField("Build.TYPE") { Build.TYPE = it }
applyStringField("Build.USER") { Build.USER = it }
applyStringField("Build.VERSION.CODENAME") { Build.VERSION.CODENAME = it }
applyStringField("Build.VERSION.INCREMENTAL") { Build.VERSION.INCREMENTAL = it }
applyStringField("Build.VERSION.RELEASE") { Build.VERSION.RELEASE = it }
applyStringField("Build.VERSION.SDK") { Build.VERSION.SDK = it }
applyIntField("Build.VERSION.SDK_INT") { Build.VERSION.SDK_INT = it }
if (android.os.Build.VERSION.SDK_INT > 21) {
Build.SUPPORTED_ABIS = profileData["Build.SUPPORTED_ABIS"]?.split(",")?.toTypedArray() ?: emptyArray()
}
}
private fun applyProfile(context: Context, profile: String) {
val profileData = getProfileData(context, profile, getRealData()) ?: getRealData()
applyProfileData(profileData)
Build.SERIAL = getEffectiveProfileSerial(context, profile)
Log.d(TAG, "Using Serial ${Build.SERIAL}")
}
fun setProfile(context: Context, profile: String?) {
SettingsContract.setSettings(context, Profile.getContentUri(context)) {
put(Profile.PROFILE, profile)
put(Profile.SERIAL, null as String?)
}
applyProfile(context, profile ?: PROFILE_AUTO)
}
@JvmStatic
fun ensureInitialized(context: Context) {
if (initialized) return
try {
val profile = getActiveProfile(context)
applyProfile(context, profile)
initialized = true
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}

View File

@ -131,6 +131,20 @@ object SettingsContract {
) )
} }
object Profile {
private const val id = "profile"
fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id)
fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id"
const val PROFILE = "device_profile"
const val SERIAL = "device_profile_serial"
val PROJECTION = arrayOf(
PROFILE,
SERIAL
)
}
private fun <T> withoutCallingIdentity(f: () -> T): T { private fun <T> withoutCallingIdentity(f: () -> T): T {
val identity = Binder.clearCallingIdentity() val identity = Binder.clearCallingIdentity()
try { try {

View File

@ -20,6 +20,7 @@ import org.microg.gms.settings.SettingsContract.CheckIn
import org.microg.gms.settings.SettingsContract.DroidGuard import org.microg.gms.settings.SettingsContract.DroidGuard
import org.microg.gms.settings.SettingsContract.Exposure import org.microg.gms.settings.SettingsContract.Exposure
import org.microg.gms.settings.SettingsContract.Gcm import org.microg.gms.settings.SettingsContract.Gcm
import org.microg.gms.settings.SettingsContract.Profile
import org.microg.gms.settings.SettingsContract.SafetyNet import org.microg.gms.settings.SettingsContract.SafetyNet
import org.microg.gms.settings.SettingsContract.getAuthority import org.microg.gms.settings.SettingsContract.getAuthority
import java.io.File import java.io.File
@ -65,6 +66,7 @@ class SettingsProvider : ContentProvider() {
Exposure.getContentUri(context!!) -> queryExposure(projection ?: Exposure.PROJECTION) Exposure.getContentUri(context!!) -> queryExposure(projection ?: Exposure.PROJECTION)
SafetyNet.getContentUri(context!!) -> querySafetyNet(projection ?: SafetyNet.PROJECTION) SafetyNet.getContentUri(context!!) -> querySafetyNet(projection ?: SafetyNet.PROJECTION)
DroidGuard.getContentUri(context!!) -> queryDroidGuard(projection ?: DroidGuard.PROJECTION) DroidGuard.getContentUri(context!!) -> queryDroidGuard(projection ?: DroidGuard.PROJECTION)
Profile.getContentUri(context!!) -> queryProfile(projection ?: Profile.PROJECTION)
else -> null else -> null
} }
@ -83,6 +85,7 @@ class SettingsProvider : ContentProvider() {
Exposure.getContentUri(context!!) -> updateExposure(values) Exposure.getContentUri(context!!) -> updateExposure(values)
SafetyNet.getContentUri(context!!) -> updateSafetyNet(values) SafetyNet.getContentUri(context!!) -> updateSafetyNet(values)
DroidGuard.getContentUri(context!!) -> updateDroidGuard(values) DroidGuard.getContentUri(context!!) -> updateDroidGuard(values)
Profile.getContentUri(context!!) -> updateProfile(values)
else -> return 0 else -> return 0
} }
return 1 return 1
@ -264,6 +267,27 @@ class SettingsProvider : ContentProvider() {
editor.apply() editor.apply()
} }
private fun queryProfile(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key ->
when (key) {
Profile.PROFILE -> getSettingsString(key, "auto")
Profile.SERIAL -> getSettingsString(key)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
private fun updateProfile(values: ContentValues) {
if (values.size() == 0) return
val editor = preferences.edit()
values.valueSet().forEach { (key, value) ->
when (key) {
Profile.PROFILE -> editor.putString(key, value as String?)
Profile.SERIAL -> editor.putString(key, value as String?)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
editor.apply()
}
private fun MatrixCursor.addRow( private fun MatrixCursor.addRow(
p: Array<out String>, p: Array<out String>,
valueGetter: (String) -> Any? valueGetter: (String) -> Any?

View File

@ -19,10 +19,11 @@ package org.microg.gms.auth;
import android.content.Context; import android.content.Context;
import org.microg.gms.checkin.LastCheckinInfo; import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.Build; import org.microg.gms.profile.Build;
import org.microg.gms.common.Constants; import org.microg.gms.common.Constants;
import org.microg.gms.common.HttpFormClient; import org.microg.gms.common.HttpFormClient;
import org.microg.gms.common.Utils; import org.microg.gms.common.Utils;
import org.microg.gms.profile.ProfileManager;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
@ -91,10 +92,11 @@ public class AuthRequest extends HttpFormClient.Request {
userAgent = String.format(USER_AGENT, deviceName, buildVersion); userAgent = String.format(USER_AGENT, deviceName, buildVersion);
} }
public AuthRequest build(Build build) { public AuthRequest build(Context context) {
sdkVersion = build.version_sdk_int; ProfileManager.ensureInitialized(context);
deviceName = build.device; sdkVersion = Build.VERSION.SDK_INT;
buildVersion = build.id; deviceName = Build.DEVICE;
buildVersion = Build.ID;
return this; return this;
} }
@ -111,7 +113,7 @@ public class AuthRequest extends HttpFormClient.Request {
} }
public AuthRequest fromContext(Context context) { public AuthRequest fromContext(Context context) {
build(Utils.getBuild(context)); build(context);
locale(Utils.getLocale(context)); locale(Utils.getLocale(context));
androidIdHex = Long.toHexString(LastCheckinInfo.read(context).getAndroidId()); androidIdHex = Long.toHexString(LastCheckinInfo.read(context).getAndroidId());
return this; return this;

View File

@ -16,13 +16,15 @@
package org.microg.gms.checkin; package org.microg.gms.checkin;
import android.content.Context;
import android.util.Log; import android.util.Log;
import org.microg.gms.common.Build;
import org.microg.gms.common.DeviceConfiguration; import org.microg.gms.common.DeviceConfiguration;
import org.microg.gms.common.DeviceIdentifier; import org.microg.gms.common.DeviceIdentifier;
import org.microg.gms.common.PhoneInfo; import org.microg.gms.common.PhoneInfo;
import org.microg.gms.common.Utils; import org.microg.gms.common.Utils;
import org.microg.gms.profile.Build;
import org.microg.gms.profile.ProfileManager;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -76,29 +78,30 @@ public class CheckinClient {
return response; return response;
} }
public static CheckinRequest makeRequest(Build build, DeviceConfiguration deviceConfiguration, public static CheckinRequest makeRequest(Context context, DeviceConfiguration deviceConfiguration,
DeviceIdentifier deviceIdent, PhoneInfo phoneInfo, DeviceIdentifier deviceIdent, PhoneInfo phoneInfo,
LastCheckinInfo checkinInfo, Locale locale, LastCheckinInfo checkinInfo, Locale locale,
List<Account> accounts) { List<Account> accounts) {
ProfileManager.ensureInitialized(context);
CheckinRequest.Builder builder = new CheckinRequest.Builder() CheckinRequest.Builder builder = new CheckinRequest.Builder()
.accountCookie(new ArrayList<>()) .accountCookie(new ArrayList<>())
.androidId(checkinInfo.getAndroidId()) .androidId(checkinInfo.getAndroidId())
.checkin(new CheckinRequest.Checkin.Builder() .checkin(new CheckinRequest.Checkin.Builder()
.build(new CheckinRequest.Checkin.Build.Builder() .build(new CheckinRequest.Checkin.Build.Builder()
.bootloader(build.bootloader) .bootloader(Build.BOOTLOADER)
.brand(build.brand) .brand(Build.BRAND)
.clientId("android-google") .clientId("android-google")
.device(build.device) .device(Build.DEVICE)
.fingerprint(build.fingerprint) .fingerprint(Build.FINGERPRINT)
.hardware(build.hardware) .hardware(Build.HARDWARE)
.manufacturer(build.manufacturer) .manufacturer(Build.MANUFACTURER)
.model(build.model) .model(Build.MODEL)
.otaInstalled(false) // TODO? .otaInstalled(false) // TODO?
//.packageVersionCode(Constants.MAX_REFERENCE_VERSION) //.packageVersionCode(Constants.MAX_REFERENCE_VERSION)
.product(build.product) .product(Build.PRODUCT)
.radio(build.radio) .radio(Build.RADIO)
.sdkVersion(build.version_sdk_int) .sdkVersion(Build.VERSION.SDK_INT)
.time(build.time / 1000) .time(Build.TIME / 1000)
.build()) .build())
.cellOperator(phoneInfo.cellOperator) .cellOperator(phoneInfo.cellOperator)
.event(Collections.singletonList(new CheckinRequest.Checkin.Event.Builder() .event(Collections.singletonList(new CheckinRequest.Checkin.Event.Builder()
@ -137,7 +140,7 @@ public class CheckinClient {
.loggingId(new Random().nextLong()) // TODO: static .loggingId(new Random().nextLong()) // TODO: static
.meid(deviceIdent.meid) .meid(deviceIdent.meid)
.otaCert(Collections.singletonList("71Q6Rn2DDZl1zPDVaaeEHItd")) .otaCert(Collections.singletonList("71Q6Rn2DDZl1zPDVaaeEHItd"))
.serial(build.serial) .serial(Build.SERIAL)
.timeZone(TimeZone.getDefault().getID()) .timeZone(TimeZone.getDefault().getID())
.userName((String) TODO) .userName((String) TODO)
.userSerialNumber((Integer) TODO) .userSerialNumber((Integer) TODO)

View File

@ -56,7 +56,7 @@ public class CheckinManager {
accounts.add(new CheckinClient.Account(account.name, token)); accounts.add(new CheckinClient.Account(account.name, token));
} }
} }
CheckinRequest request = CheckinClient.makeRequest(Utils.getBuild(context), CheckinRequest request = CheckinClient.makeRequest(context,
new DeviceConfiguration(context), Utils.getDeviceIdentifier(context), new DeviceConfiguration(context), Utils.getDeviceIdentifier(context),
Utils.getPhoneInfo(context), info, Utils.getLocale(context), accounts); Utils.getPhoneInfo(context), info, Utils.getLocale(context), accounts);
return handleResponse(context, CheckinClient.request(request)); return handleResponse(context, CheckinClient.request(request));

View File

@ -41,7 +41,7 @@ public class PushRegisterManager {
RegisterResponse response = new RegisterResponse(); RegisterResponse response = new RegisterResponse();
try { try {
response = new RegisterRequest() response = new RegisterRequest()
.build(Utils.getBuild(context)) .build(context)
.sender(sender) .sender(sender)
.info(info) .info(info)
.checkin(LastCheckinInfo.read(context)) .checkin(LastCheckinInfo.read(context))

View File

@ -16,12 +16,14 @@
package org.microg.gms.gcm; package org.microg.gms.gcm;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import org.microg.gms.checkin.LastCheckinInfo; import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.Build;
import org.microg.gms.common.HttpFormClient; import org.microg.gms.common.HttpFormClient;
import org.microg.gms.profile.Build;
import org.microg.gms.profile.ProfileManager;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -103,9 +105,10 @@ public class RegisterRequest extends HttpFormClient.Request {
return this; return this;
} }
public RegisterRequest build(Build build) { public RegisterRequest build(Context context) {
deviceName = build.device; ProfileManager.ensureInitialized(context);
buildVersion = build.id; deviceName = Build.DEVICE;
buildVersion = Build.ID;
return this; return this;
} }

View File

@ -13,10 +13,11 @@ import android.content.pm.Signature;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import org.microg.gms.common.Build;
import org.microg.gms.common.Constants; import org.microg.gms.common.Constants;
import org.microg.gms.common.PackageUtils; import org.microg.gms.common.PackageUtils;
import org.microg.gms.common.Utils; import org.microg.gms.common.Utils;
import org.microg.gms.profile.Build;
import org.microg.gms.profile.ProfileManager;
import org.microg.gms.snet.AttestRequest; import org.microg.gms.snet.AttestRequest;
import org.microg.gms.snet.AttestResponse; import org.microg.gms.snet.AttestResponse;
import org.microg.gms.snet.FileState; import org.microg.gms.snet.FileState;
@ -154,6 +155,7 @@ public class Attestation {
} }
private AttestResponse attest(AttestRequest request, String apiKey) throws IOException { private AttestResponse attest(AttestRequest request, String apiKey) throws IOException {
ProfileManager.ensureInitialized(context);
String requestUrl = SafetyNetPrefs.get(context).getServiceUrl() + "?alt=PROTO&key=" + apiKey; String requestUrl = SafetyNetPrefs.get(context).getServiceUrl() + "?alt=PROTO&key=" + apiKey;
HttpURLConnection connection = (HttpURLConnection) new URL(requestUrl).openConnection(); HttpURLConnection connection = (HttpURLConnection) new URL(requestUrl).openConnection();
connection.setRequestMethod("POST"); connection.setRequestMethod("POST");
@ -163,8 +165,7 @@ public class Attestation {
connection.setRequestProperty("Accept-Encoding", "gzip"); connection.setRequestProperty("Accept-Encoding", "gzip");
connection.setRequestProperty("X-Android-Package", packageName); connection.setRequestProperty("X-Android-Package", packageName);
connection.setRequestProperty("X-Android-Cert", PackageUtils.firstSignatureDigest(context, packageName)); connection.setRequestProperty("X-Android-Cert", PackageUtils.firstSignatureDigest(context, packageName));
Build build = Utils.getBuild(context); connection.setRequestProperty("User-Agent", "SafetyNet/" + Constants.GMS_VERSION_CODE + " (" + Build.DEVICE + " " + Build.ID + "); gzip");
connection.setRequestProperty("User-Agent", "SafetyNet/" + Constants.GMS_VERSION_CODE + " (" + build.device + " " + build.id + "); gzip");
OutputStream os = connection.getOutputStream(); OutputStream os = connection.getOutputStream();
os.write(request.encode()); os.write(request.encode());

View File

@ -24,7 +24,7 @@ import com.google.android.gms.wearable.ConnectionConfiguration;
import com.google.android.gms.wearable.internal.MessageEventParcelable; import com.google.android.gms.wearable.internal.MessageEventParcelable;
import org.microg.gms.checkin.LastCheckinInfo; import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.Build; import org.microg.gms.profile.Build;
import org.microg.wearable.ServerMessageListener; import org.microg.wearable.ServerMessageListener;
import org.microg.wearable.proto.AckAsset; import org.microg.wearable.proto.AckAsset;
import org.microg.wearable.proto.Connect; import org.microg.wearable.proto.Connect;
@ -48,7 +48,7 @@ public class MessageHandler extends ServerMessageListener {
private String peerNodeId; private String peerNodeId;
public MessageHandler(WearableImpl wearable, ConnectionConfiguration config) { public MessageHandler(WearableImpl wearable, ConnectionConfiguration config) {
this(wearable, config, new Build().model, config.nodeId, LastCheckinInfo.read(wearable.getContext()).getAndroidId()); this(wearable, config, Build.MODEL, config.nodeId, LastCheckinInfo.read(wearable.getContext()).getAndroidId());
} }
private MessageHandler(WearableImpl wearable, ConnectionConfiguration config, String name, String networkId, long androidId) { private MessageHandler(WearableImpl wearable, ConnectionConfiguration config, String name, String networkId, long androidId) {

View File

@ -148,7 +148,7 @@ class PushRegisterService : LifecycleService() {
Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent!!.extras) Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent!!.extras)
val bundle = completeRegisterRequest(this, database, val bundle = completeRegisterRequest(this, database,
RegisterRequest() RegisterRequest()
.build(Utils.getBuild(this)) .build(this)
.sender(intent.getStringExtra(EXTRA_SENDER)) .sender(intent.getStringExtra(EXTRA_SENDER))
.checkin(LastCheckinInfo.read(this)) .checkin(LastCheckinInfo.read(this))
.app(packageName) .app(packageName)
@ -164,7 +164,7 @@ class PushRegisterService : LifecycleService() {
val packageName = intent.appPackageName ?: throw RuntimeException("No package provided") val packageName = intent.appPackageName ?: throw RuntimeException("No package provided")
Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.extras) Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.extras)
val bundle = completeRegisterRequest(this, database, RegisterRequest() val bundle = completeRegisterRequest(this, database, RegisterRequest()
.build(Utils.getBuild(this)) .build(this)
.sender(intent.getStringExtra(EXTRA_SENDER)) .sender(intent.getStringExtra(EXTRA_SENDER))
.checkin(LastCheckinInfo.read(this)) .checkin(LastCheckinInfo.read(this))
.app(packageName) .app(packageName)
@ -314,7 +314,7 @@ internal class PushRegisterHandler(private val context: Context, private val dat
if (!delete) ensureAppRegistrationAllowed(context, database, packageName) if (!delete) ensureAppRegistrationAllowed(context, database, packageName)
val bundle = completeRegisterRequest(context, database, val bundle = completeRegisterRequest(context, database,
RegisterRequest() RegisterRequest()
.build(Utils.getBuild(context)) .build(context)
.sender(sender) .sender(sender)
.checkin(LastCheckinInfo.read(context)) .checkin(LastCheckinInfo.read(context))
.app(packageName) .app(packageName)

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2021, microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<profile name="Google Nexus 5X (Android 8.1.0)" device="bullhead" sdk="27" id="bullhead_27">
<!-- Data from OPM3.171019.016, Mar 2018 -->
<data key="Build.BOARD" value="bullhead" />
<data key="Build.BOOTLOADER" value="BHZ31b" />
<data key="Build.BRAND" value="google" />
<data key="Build.CPU_ABI" value="arm64-v8a" />
<data key="Build.CPU_ABI2" value="" />
<data key="Build.DEVICE" value="bullhead" />
<data key="Build.DISPLAY" value="bullhead-user 8.1.0 OPM3.171019.016 4565142 release-keys" />
<data key="Build.FINGERPRINT" value="google/bullhead/bullhead:8.1.0/OPM3.171019.016/4565142:user/release-keys" />
<data key="Build.HARDWARE" value="bullhead" />
<data key="Build.HOST" value="wpdt4.hot.corp.google.com" />
<data key="Build.ID" value="OPM3.171019.016" />
<data key="Build.MANUFACTURER" value="LGE" />
<data key="Build.MODEL" value="Nexus 5X" />
<data key="Build.PRODUCT" value="bullhead" />
<data key="Build.RADIO" value="unknown" />
<data key="Build.TAGS" value="release-keys" />
<data key="Build.TIME" value="1516849845000" />
<data key="Build.TYPE" value="user" />
<data key="Build.USER" value="android-build" />
<data key="Build.VERSION.CODENAME" value="REL" />
<data key="Build.VERSION.INCREMENTAL" value="6d95f5a143" />
<data key="Build.VERSION.RELEASE" value="8.1.0" />
<data key="Build.VERSION.SDK" value="27" />
<data key="Build.VERSION.SDK_INT" value="27" />
<data key="Build.SUPPORTED_ABIS" value="arm64-v8a,armeabi-v7a,armeabi" />
<serial template="005b56ffff999999" />
</profile>

View File

@ -40,7 +40,7 @@ public abstract class TracingIntentService extends IntentService {
public PackageInfo getPackageInfo(@NonNull String packageName, int flags) { public PackageInfo getPackageInfo(@NonNull String packageName, int flags) {
PackageInfo packageInfo = super.getPackageInfo(packageName, flags); PackageInfo packageInfo = super.getPackageInfo(packageName, flags);
if ("com.google.android.gms".equals(packageName)) { if ("com.google.android.gms".equals(packageName)) {
VersionUtil versionUtil = new VersionUtil(TracingIntentService.this, new org.microg.gms.common.Build()); VersionUtil versionUtil = new VersionUtil(TracingIntentService.this);
packageInfo.versionCode = versionUtil.getVersionCode(); packageInfo.versionCode = versionUtil.getVersionCode();
packageInfo.versionName = versionUtil.getVersionString(); packageInfo.versionName = versionUtil.getVersionString();
packageInfo.sharedUserId = "com.google.uid.shared"; packageInfo.sharedUserId = "com.google.uid.shared";

View File

@ -14,8 +14,9 @@ import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
import dalvik.system.DexClassLoader import dalvik.system.DexClassLoader
import okio.ByteString.Companion.decodeHex import okio.ByteString.Companion.decodeHex
import okio.ByteString.Companion.of import okio.ByteString.Companion.of
import org.microg.gms.common.Build
import org.microg.gms.droidguard.core.BuildConfig import org.microg.gms.droidguard.core.BuildConfig
import org.microg.gms.profile.Build
import org.microg.gms.profile.ProfileManager
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.security.MessageDigest import java.security.MessageDigest
@ -25,10 +26,9 @@ import com.android.volley.Request as VolleyRequest
import com.android.volley.Response as VolleyResponse import com.android.volley.Response as VolleyResponse
class HandleProxyFactory(private val context: Context) { class HandleProxyFactory(private val context: Context) {
private val build: Build = Build()
private val classMap = hashMapOf<String, Class<*>>() private val classMap = hashMapOf<String, Class<*>>()
private val dgDb: DgDatabaseHelper = DgDatabaseHelper(context) private val dgDb: DgDatabaseHelper = DgDatabaseHelper(context)
private val version = VersionUtil(context, build) private val version = VersionUtil(context)
private val queue = Volley.newRequestQueue(context) private val queue = Volley.newRequestQueue(context)
fun createHandle(packageName: String, flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy { fun createHandle(packageName: String, flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
@ -55,40 +55,42 @@ class HandleProxyFactory(private val context: Context) {
} }
private fun readFromDatabase(flow: String?): Triple<String, ByteArray, ByteArray>? { private fun readFromDatabase(flow: String?): Triple<String, ByteArray, ByteArray>? {
val id = "$flow/${version.versionString}/${build.fingerprint}" ProfileManager.ensureInitialized(context)
val id = "$flow/${version.versionString}/${Build.FINGERPRINT}"
return dgDb.get(id) return dgDb.get(id)
} }
fun createRequest(flow: String?, packageName: String, pingData: PingData? = null, extra: ByteArray? = null): Request { fun createRequest(flow: String?, packageName: String, pingData: PingData? = null, extra: ByteArray? = null): Request {
ProfileManager.ensureInitialized(context)
return Request( return Request(
usage = Usage(flow, packageName), usage = Usage(flow, packageName),
info = listOf( info = listOf(
KeyValuePair("BOARD", build.board), KeyValuePair("BOARD", Build.BOARD),
KeyValuePair("BOOTLOADER", build.bootloader), KeyValuePair("BOOTLOADER", Build.BOOTLOADER),
KeyValuePair("BRAND", build.brand), KeyValuePair("BRAND", Build.BRAND),
KeyValuePair("CPU_ABI", build.cpu_abi), KeyValuePair("CPU_ABI", Build.CPU_ABI),
KeyValuePair("CPU_ABI2", build.cpu_abi2), KeyValuePair("CPU_ABI2", Build.CPU_ABI2),
KeyValuePair("SUPPORTED_ABIS", build.supported_abis.joinToString(",")), KeyValuePair("SUPPORTED_ABIS", Build.SUPPORTED_ABIS.joinToString(",")),
KeyValuePair("DEVICE", build.device), KeyValuePair("DEVICE", Build.DEVICE),
KeyValuePair("DISPLAY", build.display), KeyValuePair("DISPLAY", Build.DISPLAY),
KeyValuePair("FINGERPRINT", build.fingerprint), KeyValuePair("FINGERPRINT", Build.FINGERPRINT),
KeyValuePair("HARDWARE", build.hardware), KeyValuePair("HARDWARE", Build.HARDWARE),
KeyValuePair("HOST", build.host), KeyValuePair("HOST", Build.HOST),
KeyValuePair("ID", build.id), KeyValuePair("ID", Build.ID),
KeyValuePair("MANUFACTURER", build.manufacturer), KeyValuePair("MANUFACTURER", Build.MANUFACTURER),
KeyValuePair("MODEL", build.model), KeyValuePair("MODEL", Build.MODEL),
KeyValuePair("PRODUCT", build.product), KeyValuePair("PRODUCT", Build.PRODUCT),
KeyValuePair("RADIO", build.radio), KeyValuePair("RADIO", Build.RADIO),
KeyValuePair("SERIAL", build.serial), KeyValuePair("SERIAL", Build.SERIAL),
KeyValuePair("TAGS", build.tags), KeyValuePair("TAGS", Build.TAGS),
KeyValuePair("TIME", build.time.toString()), KeyValuePair("TIME", Build.TIME.toString()),
KeyValuePair("TYPE", build.type), KeyValuePair("TYPE", Build.TYPE),
KeyValuePair("USER", build.user), KeyValuePair("USER", Build.USER),
KeyValuePair("VERSION.CODENAME", build.version_codename), KeyValuePair("VERSION.CODENAME", Build.VERSION.CODENAME),
KeyValuePair("VERSION.INCREMENTAL", build.version_incremental), KeyValuePair("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL),
KeyValuePair("VERSION.RELEASE", build.version_release), KeyValuePair("VERSION.RELEASE", Build.VERSION.RELEASE),
KeyValuePair("VERSION.SDK", build.version_sdk), KeyValuePair("VERSION.SDK", Build.VERSION.SDK),
KeyValuePair("VERSION.SDK_INT", build.version_sdk_int.toString()), KeyValuePair("VERSION.SDK_INT", Build.VERSION.SDK_INT.toString()),
), ),
versionName = version.versionString, versionName = version.versionString,
versionCode = BuildConfig.VERSION_CODE, versionCode = BuildConfig.VERSION_CODE,
@ -107,6 +109,7 @@ class HandleProxyFactory(private val context: Context) {
} }
fun fetchFromServer(flow: String?, request: Request): Triple<String, ByteArray, ByteArray> { fun fetchFromServer(flow: String?, request: Request): Triple<String, ByteArray, ByteArray> {
ProfileManager.ensureInitialized(context)
val future = RequestFuture.newFuture<SignedResponse>() val future = RequestFuture.newFuture<SignedResponse>()
queue.add(object : VolleyRequest<SignedResponse>(Method.POST, SERVER_URL, future) { queue.add(object : VolleyRequest<SignedResponse>(Method.POST, SERVER_URL, future) {
override fun parseNetworkResponse(response: NetworkResponse): VolleyResponse<SignedResponse> { override fun parseNetworkResponse(response: NetworkResponse): VolleyResponse<SignedResponse> {
@ -146,11 +149,13 @@ class HandleProxyFactory(private val context: Context) {
throw IllegalStateException() throw IllegalStateException()
} }
} }
val id = "$flow/${version.versionString}/${build.fingerprint}" val id = "$flow/${version.versionString}/${Build.FINGERPRINT}"
val expiry = (response.expiryTimeSecs ?: 0).toLong() val expiry = (response.expiryTimeSecs ?: 0).toLong()
val byteCode = response.byteCode!!.toByteArray() val byteCode = response.byteCode?.toByteArray() ?: ByteArray(0)
val extra = response.extra!!.toByteArray() val extra = response.extra?.toByteArray() ?: ByteArray(0)
dgDb.put(id, expiry, vmKey, byteCode, extra) if (response.save != false) {
dgDb.put(id, expiry, vmKey, byteCode, extra)
}
return Triple(vmKey, byteCode, extra) return Triple(vmKey, byteCode, extra)
} }

View File

@ -6,14 +6,16 @@
package org.microg.gms.droidguard package org.microg.gms.droidguard
import android.content.Context import android.content.Context
import org.microg.gms.common.Build
import org.microg.gms.droidguard.core.BuildConfig import org.microg.gms.droidguard.core.BuildConfig
import org.microg.gms.profile.Build
import org.microg.gms.profile.ProfileManager
class VersionUtil(private val context: Context, private val build: Build = Build()) { class VersionUtil(private val context: Context) {
val buildType: String val buildType: String
get() { get() {
ProfileManager.ensureInitialized(context)
// Note: Android TV and Watch use different version codes // Note: Android TV and Watch use different version codes
val versionCode = when (build.version_sdk_int) { val versionCode = when (Build.VERSION.SDK_INT) {
31 -> "19" 31 -> "19"
30 -> "15" 30 -> "15"
29 -> "12" 29 -> "12"
@ -22,14 +24,14 @@ class VersionUtil(private val context: Context, private val build: Build = Build
21, 22 -> "02" 21, 22 -> "02"
else -> "00" else -> "00"
} }
val architectureCode = when (build.cpu_abi) { val architectureCode = when (Build.CPU_ABI) {
"x86_64" -> "08" "x86_64" -> "08"
"x86" -> "07" "x86" -> "07"
"arm64-v8a" -> "04" "arm64-v8a" -> "04"
"arm", "armeabi", "armeabi-v7a" -> "03" "arm", "armeabi", "armeabi-v7a" -> "03"
else -> "00" else -> "00"
} }
val dpiCode = when (context.resources.displayMetrics.densityDpi) { val dpiCode = when (context.resources.displayMetrics.densityDpi) { // TODO: Also something to get from profile
160 -> "02" 160 -> "02"
240 -> "04" 240 -> "04"
320 -> "06" 320 -> "06"