Android management API autoUpdateMode not applied - android

I've implemented a management server via this library which google provides.
com.google.apis:google-api-services-androidmanagement:v1-rev20211122-1.32.1
My policy looks as below.
Policy()
.setApplications(
Collections.singletonList(
ApplicationPolicy()
.setPackageName(getAppPackageByHotelSeq(hotelSeq))
.setManagedConfiguration(getPolicyParams(policyName))
.setInstallType("KIOSK")
.setDefaultPermissionPolicy("GRANT")
.setPermissionGrants(
mutableListOf(
PermissionGrant().setPermission("android.permission.ACCESS_NETWORK_STATE")
.setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS")
.setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.CAMERA").setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.ACCESS_COARSE_LOCATION")
.setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.ACCESS_FINE_LOCATION")
.setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.WAKE_LOCK").setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.WRITE_EXTERNAL_STORAGE")
.setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.READ_EXTERNAL_STORAGE")
.setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.READ_PHONE_STATE").setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.ACCESS_WIFI_STATE").setPolicy("GRANT"),
PermissionGrant().setPermission("android.permission.RECORD_AUDIO").setPolicy("GRANT")
)
)
.setLockTaskAllowed(true)
**.setAutoUpdateMode("AUTO_UPDATE_HIGH_PRIORITY")**
)
)
.setPersistentPreferredActivities(
Collections.singletonList(
PersistentPreferredActivity()
.setReceiverActivity(getAppPackageByHotelSeq(hotelSeq))
.setActions(
Collections.singletonList("android.intent.action.MAIN")
)
.setCategories(categories)
)
)
.setSystemUpdate(SystemUpdate().setType("WINDOWED").setStartMinutes(0).setEndMinutes(1439))
.setKeyguardDisabled(true)
.setDebuggingFeaturesAllowed(true)
.setStatusBarDisabled(true)
.setStatusReportingSettings(StatusReportingSettings().apply {
applicationReportsEnabled = false
deviceSettingsEnabled = true
softwareInfoEnabled = true
memoryInfoEnabled = false
networkInfoEnabled = true
displayInfoEnabled = true
powerManagementEventsEnabled = true
hardwareStatusEnabled = false
systemPropertiesEnabled = true
applicationReportingSettings = ApplicationReportingSettings()
}).apply {
if (!enablePower) {
this.kioskCustomization = KioskCustomization().setPowerButtonActions("POWER_BUTTON_BLOCKED")
}
}
According to the documentation(https://developers.google.com/android/management/control-app-updates), the line
.setAutoUpdateMode("AUTO_UPDATE_HIGH_PRIORITY")
is supposed to update my target app immediately after an update is posted. However, it isn't happenning.
If I add this line and apply the policy,
minimumVersionCode = 'my newest version code as integer'
A warning popup is shown almost instantly that the app version is low, and without a manual update, the device will be deleted in 9 days. (After 10~15 minutes after this threatening popup, the update seems to kick in.)
What can I do to solve this issue?

Related

when using Navigation got error "skipped x frames! The application may be doing too much work on its main thread."

I/Choreographer: Skipped 204 frames! The application may be doing too much work on its main thread.
i saw some same questions here but none of them seems helping in my case
I'm trying to build a music streaming app, files are stored in Firestore storage music is played using Media3, The actions of music player are controlled using Interface
Just like any music app There will be list of songs when clicked it goes to new Screen to control the playback.When i did all this in one screen it worked just fine(track list and Play Button in One Screen)
but after i implemented Navigation and seperated track list and PlayerController in 2 screen(PlayerController show up when clicked any one of trackList) it started to skipping frames
this is how i managed network calls
class TrackRepository {
private val storage = Firebase.storage
private val trackRef = storage.reference
suspend fun getTracks() = suspendCoroutine<List<Track>> { result ->
val trackList = mutableListOf<Track>()
try {
Firebase.firestore.collection(Constants.TRACK).get().addOnCompleteListener { task ->
var index = 0
task.result.forEach { document ->
val trackUrl = trackRef.child(document.getString(Constants.FILENAME)!!)
trackUrl.downloadUrl.addOnSuccessListener { trackDownloadUrl ->
trackList.add(
document.toTrack(
trackDownloadUrl.toString()
)
)
if (index == task.result.size() - 1) {
result.resume(trackList)
}
index++
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
&
#HiltViewModel
class TrackViewModel #Inject constructor(trackRepository: TrackRepository): ViewModel() {
private val _trackList = MutableLiveData<List<Track>>()
val trackList : LiveData<List<Track>> get() = _trackList
init {
viewModelScope.launch() {
trackRepository.getTracks().let {
_trackList.postValue(it.sortedBy { it.fileName })
}
}
}
}
this is my PlayerScreen(screen which stopped working) under NavHost
composable("player_screen"){
val result = navController.previousBackStackEntry?.savedStateHandle?.get<MyTrack>("track")
PlayerScreen(
navController = navController,
onMusicPlayerClick = onMusicPlayerClick,
track = result?.track!!,
onTrackItemClick = onTrackItemClick,
trackList = trackList,
isPlaying = isPlaying,
)
}
this is how new screen is called from track list screen
if (selected){
val newTrack = MyTrack(track, navController, onMusicPlayerClick)
navController.currentBackStackEntry?.savedStateHandle?.set(
key = "track",
value = newTrack
)
onTrackItemClick(track)
navController.navigate("player_screen")
}
it says too much work in main thread but i dont understand what the long running task in my app If it is caused by network calls it would have skipped frame when tried without navigation and Media3 will manage its thread in internal, they suggest to use Main thread for Media3.The only task left as a long running task was Downloader for offline use(starts download if Current Playing song is not available in mobile storage) so i removed Downloader from my code(removed downloader for offline use and ran completely on internet) and still kept skipping frames
i was abled to completely control this error by surrounding
navController.navigate("player_screen")
in
LaunchedEffect(Unit)
but i think its not a good solution and it led to some other issues
also i tried Android Profiler to find error but i couldnt understand anything from those charts
these are some Result it shows in android profiler
(the hidden texts are app name(MainActivity))
The Problem is solved. It was caused by bad usage of remember{mutableStateOf()}
if you have same error
do what error just told you: which is doing less work in main thread
use Android Profiler to detect heavy task check out ->https://developer.android.com/studio/profile/jank-detection
check your resource folder. Inappropriate resizing can also trigger this error

animateFloatAsState sometimes creates runtime type error on Jetpack Compose

The code below (ImageFace()) works very well nearly all of the time. However, some times, without any interaction at all, I will get a fatal error posted above. This happens when I leave my app running on the emulator for a few hours (but sometimes it happens instantly). And once I run the app again, the error again vanishes for the next few hours/days.
Part of the code that creates this error:
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun ImageFace(image: String = "placeholder.png", onTap: () -> Unit = {}) {
val selected = remember { mutableStateOf(false) }
val scale = animateFloatAsState(if (selected.value) 0.8f else 1f)
Column(Modifier.scale(scale.value)) {
Image(
// other attributes
modifier = Modifier
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> {
selected.value = true
}
MotionEvent.ACTION_UP -> {
selected.value = false
onTap()
}
MotionEvent.ACTION_CANCEL -> {
selected.value = false
}
}
true
}
)
}
}
Said error:
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Number
at com.example.simplephone.LiveLiterals$MainActivityKt.Float$else$if$arg-0$call-animateFloatAsState$val-scale$fun-ImageFace(Unknown Source:31)
at com.example.simplephone.MainActivityKt.ImageFace(MainActivity.kt:323)
To the extent that I understand, this could be an issue with animateFloatAsState not always returning State<Float>. But I'm new to this environment, so it is very likely that I'm not using this as intended.
Also the MainActivity.kt:323 is a random line number inside MainActivity which changes each time this error occurs. Debugging this has been impossible since it happens super randomly. Any pointers in the right direction would be appreciated.
Seems like your Android Studio resources got mixed, try to do next:
Build -> Clean Project -> Rebuild Project

Wrong If check statement

I wanted to make a method that determine if the application is started for the very first time, no matter the current version of the application. People suggest that we should use SharedPreferences as seen from this qustion. Below is the function that determine if application is started for the very first time.
companion object {
const val APP_LAUNCH_FIRST_TIME: Int = 0 // first start ever
const val APP_LAUNCH_FIRST_TIME_VERSION: Int = 1 // first start in this version (when app is updated)
const val APP_LAUNCH_NORMAL: Int = 2 // normal app start
/**
* Method that checks if the application is started for the very first time, or for the first time
* of the updated version, or just normal start.
*/
fun checkForFirstAppStart(context: Context): Int {
val sharedPreferencesVersionTag = "last_app_version"
val sharedPreferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(context)
var appStart = APP_LAUNCH_NORMAL
try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val lastVersionCode = sharedPreferences.getLong(sharedPreferencesVersionTag, -1L)
val currentVersionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
appStart = when {
lastVersionCode == -1L -> APP_LAUNCH_FIRST_TIME
lastVersionCode < currentVersionCode -> APP_LAUNCH_FIRST_TIME_VERSION
lastVersionCode > currentVersionCode -> APP_LAUNCH_NORMAL
else -> APP_LAUNCH_NORMAL
}
// Update version in preferences
sharedPreferences.edit().putLong(sharedPreferencesVersionTag, currentVersionCode).commit()
} catch (e: PackageManager.NameNotFoundException) {
// Unable to determine current app version from package manager. Defensively assuming normal app start
}
return appStart
}
}
Now in my MainActivity I make the check in this way, but strangely enough I always end up inside the if statement, although appLaunch is different from MainActivityHelper.APP_LAUNCH_FIRST_TIME
val appLaunch = MainActivityHelper.checkForFirstAppStart(this)
if (appLaunch == MainActivityHelper.APP_LAUNCH_FIRST_TIME) {
val c = 299_792_458L
}
Here we see that appLaunch is 2
Here we see that MainActivityHelper.APP_LAUNCH_FIRST_TIME is 0
I am in the main thread I check using Thread.currentThread(), and when I add watches in the debugger (appLaunch == MainActivityHelper.APP_LAUNCH_FIRST_TIME) I get false.
So I suggest that there is some delay, and by the time the if check is made the result is changed?
There's nothing wrong with the code. I tested it and it works as intended. I get all three return values depending on the circumstances. I simplified the code a bit but the original code should nevertheless works.
enum class AppLaunch {
LAUNCH_FIRST_TIME, // first start ever
FIRST_TIME_VERSION, // first start in this version (when app is updated)
NORMAL // normal app start
}
/**
* Method that checks if the application is started for the very first time, or for the first time
* of the updated version, or just normal start.
*/
fun checkForFirstAppStart(context: Context): AppLaunch {
val sharedPreferencesVersionTag = "last_app_version"
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
return try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val lastVersionCode = sharedPreferences.getLong(sharedPreferencesVersionTag, -1L)
val currentVersionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
// Update version in preferences
sharedPreferences.edit().putLong(sharedPreferencesVersionTag, currentVersionCode).commit()
when (lastVersionCode) {
-1L -> AppLaunch.LAUNCH_FIRST_TIME
in 0L until currentVersionCode -> AppLaunch.FIRST_TIME_VERSION
else -> AppLaunch.NORMAL
}
} catch (e: PackageManager.NameNotFoundException) {
// Unable to determine current app version from package manager. Defensively assuming normal app start
AppLaunch.NORMAL
}
}
I experimented a bit and the issue you see looks like a bug in Android Studio. If the code in the if statement is a NOP (no operation) then the debugger seems to stop there. If the code does have a side effect, the debugger doesn't stop.
Things like this can be infuriating but with Android, Android Studio and the tooling, bugs like this are pretty common (unfortunately).
if (appLaunch == APP_LAUNCH_FIRST_TIME) {
val c = 299_792_458L
}
translates to the following byte code:
L3 (the if statement)
LINENUMBER 32 L3
ILOAD 4
IFNE L4
L5
LINENUMBER 33 L5
LDC 299792458
LSTORE 2
Converting c to a var
var c = 1L
if (appLaunch == APP_LAUNCH_FIRST_TIME) {
c = 299_792_458L
}
results in identical byte code so it's certainly not a code problem but an issue with Android Studio.
Update
If you need fast writes with enums you can use something like this:
fun appLaunchById(id: Int, def: AppLaunch = AppLaunch.NORMAL) = AppLaunch.values().find { it.id == id } ?: def
enum class AppLaunch(val id: Int) {
LAUNCH_FIRST_TIME(0), // first start ever
FIRST_TIME_VERSION(1), // first start in this version (when app is updated)
NORMAL(2); // normal app start
}
^^^ writes an Int so fast and short. Reading is certainly not super fast though.
Update 2
Generic version of the enum solution:
inline fun <reified T : Enum<*>> enumById(hash: Int, def: T) = enumValues<T>()
.find { it.hashCode() == hash }
?: def
enum class AppLaunch {
LAUNCH_FIRST_TIME, // first start ever
FIRST_TIME_VERSION, // first start in this version (when app is updated)
NORMAL // normal app start
}
Usage:
val enum = enumById(value.hashCode(), AppLaunch.NORMAL)

Using AuroraStore code, how does it get the "library" (history of installed apps), and how to get the time they were installed?

Background
In the past, I've found a special app called "Purchased apps" that somehow gets a list of the apps you've purchased. Not seeing any API for this, I asked how does it do it (and sadly still couldn't find a clear answer and a POC to demonstrate it).
The problem
Time passed, and I've noticed there is actually an open sourced app called "Aurora Store" (repository here) that can get about as much information as the Play Store. Screenshot from it:
Thing is, I got issues trying to figure out how to use its code properly, and the weird thing is that those apps get the information from different sources.
What I've tried
So, seeing it allows you to login to Google, and then get the "library" information (history of installed apps), I decided to give it a go (full sample on Github, here) :
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
private val cookieManager = CookieManager.getInstance()
#SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val cachedEmail = defaultSharedPreferences.getString("email", null)
val cachedAasToken = defaultSharedPreferences.getString("aasToken", null)
if (cachedEmail != null && cachedAasToken != null) {
onGotAasToken(applicationContext, cachedEmail, cachedAasToken)
} else {
webView = findViewById(R.id.webView)
cookieManager.removeAllCookies(null)
cookieManager.acceptThirdPartyCookies(webView)
cookieManager.setAcceptThirdPartyCookies(webView, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
webView.settings.safeBrowsingEnabled = false
}
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
val cookies = CookieManager.getInstance().getCookie(url)
val cookieMap: MutableMap<String, String> = AC2DMUtil.parseCookieString(cookies)
val oauthToken: String? = cookieMap[AUTH_TOKEN]
oauthToken?.let {
webView.evaluateJavascript("(function() { return document.getElementById('profileIdentifier').innerHTML; })();") {
val email = it.replace("\"".toRegex(), "")
Log.d("AppLog", "got email?${email.isNotBlank()} got oauthToken?${oauthToken.isNotBlank()}")
buildAuthData(applicationContext, email, oauthToken)
}
} ?: Log.d("AppLog", "could not get oauthToken")
}
}
webView.settings.apply {
allowContentAccess = true
databaseEnabled = true
domStorageEnabled = true
javaScriptEnabled = true
cacheMode = WebSettings.LOAD_DEFAULT
}
webView.loadUrl(EMBEDDED_SETUP_URL)
}
}
companion object {
const val EMBEDDED_SETUP_URL =
"https://accounts.google.com/EmbeddedSetup/identifier?flowName=EmbeddedSetupAndroid"
const val AUTH_TOKEN = "oauth_token"
private fun buildAuthData(context: Context, email: String, oauthToken: String?) {
thread {
try {
val aC2DMResponse: Map<String, String> =
AC2DMTask().getAC2DMResponse(email, oauthToken)
val aasToken = aC2DMResponse["Token"]!!
PreferenceManager.getDefaultSharedPreferences(context)
.edit().putString("email", email).putString("aasToken", aasToken).apply()
onGotAasToken(context, email, aasToken)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun onGotAasToken(context: Context, email: String, aasToken: String) {
thread {
val properties = NativeDeviceInfoProvider(context).getNativeDeviceProperties()
val authData = AuthHelper.build(email, aasToken, properties)
val purchaseHelper = PurchaseHelper(authData).using(HttpClient.getPreferredClient())
var offset = 0
Log.d("AppLog", "list of purchase history:")
while (true) {
val purchaseHistory = purchaseHelper.getPurchaseHistory(offset)
if (purchaseHistory.isNullOrEmpty())
break
val size = purchaseHistory.size
offset += size
purchaseHistory.forEach {
Log.d("AppLog", "${it.packageName} ${it.displayName}")
}
}
Log.d("AppLog", "done")
}
}
}
}
It seems it got the token it needs (and the email), but sadly it seems to get 2 apps and that's it, and then when I try to get the next ones, I get the same 2 apps, twice more, meaning as such:
list of purchase history:
dev.southpaw.dungeon Dungeon Live Wallpaper
com.crydata.mylivewallpaper Hex AMOLED Neon Live Wallpaper 2021
dev.southpaw.dungeon Dungeon Live Wallpaper
com.crydata.mylivewallpaper Hex AMOLED Neon Live Wallpaper 2021
dev.southpaw.dungeon Dungeon Live Wallpaper
com.crydata.mylivewallpaper Hex AMOLED Neon Live Wallpaper 2021
and on the last time it tries to get the next chunk of apps, it crashes with this exception:
FATAL EXCEPTION: Thread-4
Process: com.lb.getplaystoreinstalledappshistory, PID: 6149
Server(code=400, reason=Bad Request)
at com.aurora.gplayapi.helpers.AppDetailsHelper.getAppByPackageName(AppDetailsHelper.kt:115)
at com.aurora.gplayapi.helpers.PurchaseHelper.getPurchaseHistory(PurchaseHelper.kt:63)
at com.lb.getplaystoreinstalledappshistory.MainActivity$Companion$onGotAasToken$1.invoke(MainActivity.kt:96)
at com.lb.getplaystoreinstalledappshistory.MainActivity$Companion$onGotAasToken$1.invoke(MainActivity.kt:68)
at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
The questions
What's wrong with how I tried to get the list of apps? How can I get it right, ordered by time installed?
Is there any way to get the time they were installed (or any clue about it) ? Somehow the "Purchased apps" app got the time. Granted it was only for purchased apps, but still...
The "Purchased apps" app even got login better, as it doesn't require user-name and password. Instead it offers a dialog to choose the account. Assuming I get it right, is it possible to get the same information using the same login dialog ?
Not sure if this information is remotely useful but might as well mention it...
I accidentally decompiled their archived APK from 2015 when they didn't minimize their code and at least back then, they were using a JSoup HTML parser spider. Possibly they still are, which is probably not allowed by Google and incredibly prone to maintenance.

DatagramSocket not receiving some packets on Android 9

I want to receive callerId callbacks in my Android application. I've followed an example provided by it's creators which is available here.
My listening function looks like this (exact copy but in Kotlin):
private fun startListening() {
val socket = DatagramSocket(null)
val address = InetSocketAddress("255.255.255.255", 3520)
socket.reuseAddress = true
socket.broadcast = true
socket.bind(address)
var looping = true
var buffer = ByteArray(65507)
while (looping) {
val dp = DatagramPacket(buffer, buffer.size)
try {
log.debug("Waiting for call")
socket.receive(dp)
val recString = String(dp.data, 0, dp.length)
log.debug("Received new message: $recString")
_callReceivedLive.postValue(recString)
} catch (e: Exception) {
log.error("Exception in CallerIdListener", e)
looping = false
}
}
}
After installing it on device with Android 5.1.1 Version everything seems fine. Every single call I mock using Ethernet Emulator is received by the application.
Now after firing this app on Samsung Galaxy Tab with Android Version 9 it only receives like 30% of calls.
Any idea what might be wrong?

Categories

Resources