From c6e1a2b7c1c6ac3591373001c9aa8e11aca7bb5b Mon Sep 17 00:00:00 2001 From: Jonas Lochmann Date: Mon, 21 Mar 2022 01:00:00 +0100 Subject: [PATCH] Disable fallback and enable new app detection by random --- .../android/data/model/ConfigurationItem.kt | 2 +- .../foregroundapp/QForegroundAppHelper.kt | 40 +--- .../usagestats/MergedUsageStatsReader.kt | 65 ------ .../foregroundapp/usagestats/ParcelBlob.kt | 88 -------- .../usagestats/ParcelUsageStatsReader.kt | 158 -------------- .../DiagnoseExperimentalFlagFragment.kt | 6 - .../diagnose/DiagnoseForegroundAppFragment.kt | 196 +++--------------- .../diagnose_foreground_app_fragment.xml | 7 - app/src/main/res/values-de/strings.xml | 2 - app/src/main/res/values/strings.xml | 2 - 10 files changed, 34 insertions(+), 532 deletions(-) delete mode 100644 app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/MergedUsageStatsReader.kt delete mode 100644 app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelBlob.kt delete mode 100644 app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelUsageStatsReader.kt diff --git a/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt b/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt index 16a2109..59294b8 100644 --- a/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt +++ b/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt @@ -249,5 +249,5 @@ object ExperimentalFlags { const val ENABLE_SOFT_BLOCKING = 16384L const val SYNC_RELATED_NOTIFICATIONS = 32768L const val INSTANCE_ID_FG_APP_DETECTION = 65536L - const val DISABLE_FG_APP_DETECTION_FALLBACK = 131072L + // private const val OBSOLETE_DISABLE_FG_APP_DETECTION_FALLBACK = 131072L } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/QForegroundAppHelper.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/QForegroundAppHelper.kt index 564a29b..2ce3c83 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/QForegroundAppHelper.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/QForegroundAppHelper.kt @@ -16,49 +16,25 @@ package io.timelimit.android.integration.platform.android.foregroundapp import android.content.Context -import android.util.Log -import io.timelimit.android.BuildConfig import io.timelimit.android.data.model.ExperimentalFlags import io.timelimit.android.integration.platform.ForegroundApp +import io.timelimit.android.integration.platform.android.foregroundapp.usagestats.DirectUsageStatsReader +import java.security.SecureRandom class QForegroundAppHelper(context: Context): UsageStatsForegroundAppHelper(context) { - companion object { - private const val LOG_TAG = "QForegroundAppHelper" - } - private val legacy = LollipopForegroundAppHelper(context) private val modern = InstanceIdForegroundAppHelper(context) - private var fallbackCounter = 0 + private val forceNewMethod = SecureRandom().nextBoolean() override suspend fun getForegroundApps( queryInterval: Long, experimentalFlags: Long ): Set { - val useInstanceIdForegroundAppDetection = experimentalFlags and ExperimentalFlags.INSTANCE_ID_FG_APP_DETECTION == ExperimentalFlags.INSTANCE_ID_FG_APP_DETECTION - val disableFallback = experimentalFlags and ExperimentalFlags.DISABLE_FG_APP_DETECTION_FALLBACK == ExperimentalFlags.DISABLE_FG_APP_DETECTION_FALLBACK + val canUseModern = DirectUsageStatsReader.instanceIdSupported + val didUserRequestModern = experimentalFlags and ExperimentalFlags.INSTANCE_ID_FG_APP_DETECTION == ExperimentalFlags.INSTANCE_ID_FG_APP_DETECTION + val useModern = forceNewMethod || didUserRequestModern - val result = if (useInstanceIdForegroundAppDetection && disableFallback) { - modern.getForegroundApps(queryInterval, experimentalFlags) - } else if (useInstanceIdForegroundAppDetection && fallbackCounter == 0) { - try { - modern.getForegroundApps(queryInterval, experimentalFlags) - } catch (ex: Exception) { - if (BuildConfig.DEBUG) { - Log.d(LOG_TAG, "falling back to the legacy implementation", ex) - } - - fallbackCounter = 100 - - legacy.getForegroundApps(queryInterval, experimentalFlags) - } - } else { - legacy.getForegroundApps(queryInterval, experimentalFlags) - } - - if (fallbackCounter > 0) { - fallbackCounter -= 1 - } - - return result + return if (canUseModern && useModern) modern.getForegroundApps(queryInterval, experimentalFlags) + else legacy.getForegroundApps(queryInterval, experimentalFlags) } } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/MergedUsageStatsReader.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/MergedUsageStatsReader.kt deleted file mode 100644 index 3691856..0000000 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/MergedUsageStatsReader.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package io.timelimit.android.integration.platform.android.foregroundapp.usagestats - -import java.lang.RuntimeException - -class MergedUsageStatsReader(private val primary: UsageStatsReader, private val confirmation: UsageStatsReader): UsageStatsReader { - override val timestamp = primary.timestamp - override val eventType = primary.eventType - override val instanceId = primary.instanceId - override val packageName = primary.packageName - override val className = primary.className - - override fun loadNextEvent(): Boolean { - val didReadEvent = kotlin.run { - val didReadPrimaryEvent = primary.loadNextEvent() - val didReadConfirmationEvent = confirmation.loadNextEvent() - - if (didReadConfirmationEvent != didReadPrimaryEvent) { - throw NotMatchingDataException( - if (didReadPrimaryEvent) "primary got next event but confirmation not" - else "confirmation got next event but primary not" - ) - } - - didReadPrimaryEvent // == didReadNativeEvent - } - - if (!didReadEvent) return false - - // check the consistency - if (primary.eventType != confirmation.eventType) { - throw NotMatchingDataException("got different eventTypes: ${primary.eventType} vs ${confirmation.eventType}") - } - - if (primary.timestamp != confirmation.timestamp) { - throw NotMatchingDataException("got different timestamps: ${primary.timestamp} vs ${confirmation.timestamp}") - } - - return true - } - - override fun free() { - try { - primary.free() - } finally { - confirmation.free() - } - } - - class NotMatchingDataException(detail: String): RuntimeException("not matching data: $detail") -} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelBlob.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelBlob.kt deleted file mode 100644 index 44dcd56..0000000 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelBlob.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package io.timelimit.android.integration.platform.android.foregroundapp.usagestats - -import android.os.Parcel -import java.io.FileInputStream - -/** - * This class implements a clone of Parcel.readBlob() as this is not a public - * interface in all Android versions that support it. - * - * The wire format is (both as integer): length, useSharedMemory - * - * useSharedMemory can be 0 or 1 - */ -object ParcelBlob { - fun readBlob(parcel: Parcel): ByteArray { - val length = parcel.readInt() - - val useSharedMemory = when (parcel.readInt()) { - 0 -> false - 1 -> true - else -> throw InvalidBooleanException() - } - - if (useSharedMemory) { - val fd = parcel.readFileDescriptor() - val result = ByteArray(length) - - try { - FileInputStream(fd.fileDescriptor).use { stream -> - var cursor = 0 - - while (cursor < length) { - val bytesRead = stream.read(result, cursor, length - cursor) - - if (bytesRead == -1) { - throw UnexpectedEndOfSharedMemory() - } else { - cursor += bytesRead - } - } - } - } finally { - fd.close() - } - - return result - } else { - val tempParcel = Parcel.obtain() - val oldPosition = parcel.dataPosition() - - try { - tempParcel.appendFrom(parcel, oldPosition, length) - - parcel.setDataPosition(oldPosition + length) - - val result = tempParcel.marshall() - - if (result.size != length) { - throw WrongReturnedDataSize() - } - - return result - } finally { - tempParcel.recycle() - } - } - } - - open class ParcelBlobException: RuntimeException() - class InvalidBooleanException: ParcelBlobException() - class UnexpectedEndOfSharedMemory: ParcelBlobException() - class WrongReturnedDataSize: ParcelBlobException() -} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelUsageStatsReader.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelUsageStatsReader.kt deleted file mode 100644 index 256b5fc..0000000 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/foregroundapp/usagestats/ParcelUsageStatsReader.kt +++ /dev/null @@ -1,158 +0,0 @@ -/* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package io.timelimit.android.integration.platform.android.foregroundapp.usagestats - -import android.app.usage.UsageEvents -import android.content.res.Configuration -import android.os.Build -import android.os.Parcel -import java.lang.RuntimeException - -/** - * This class is/should be a Parcel compatible clone of the UsageEvents - * system class. This allows reading fields which the public API does not provide. - */ -class ParcelUsageStatsReader (private val content: Parcel): UsageStatsReader { - companion object { - private const val CONFIGURATION_CHANGE = 5 - private const val SHORTCUT_INVOCATION = 8 - private const val CHOOSER_ACTION = 9 - private const val STANDBY_BUCKET_CHANGED = 11 - private const val NOTIFICATION_INTERRUPTION = 12 - private const val LOCUS_ID_SET = 30 - private const val DUMMY_STRING = "null" - - fun getParcel(input: UsageEvents): Parcel { - if (Build.VERSION.SDK_INT > 32) { - throw UntestedSystemVersionException() - } - - val outerParcel = Parcel.obtain() - - val blob = try { - input.writeToParcel(outerParcel, 0) - outerParcel.setDataPosition(0) - - ParcelBlob.readBlob(outerParcel) - } finally { - outerParcel.recycle() - } - - val innerParcel = Parcel.obtain() - - try { - innerParcel.unmarshall(blob, 0, blob.size) - innerParcel.setDataPosition(0) - - return innerParcel - } catch (ex: Exception) { - innerParcel.recycle() - - throw ex - } - } - - fun fromUsageEvents(input: UsageEvents): ParcelUsageStatsReader = ParcelUsageStatsReader( - getParcel(input) - ) - } - - private var free = false - private val length = content.readInt() - private var index = content.readInt() - private val strings = if (length > 0) content.createStringArray() else null - - init { - if (length > 0) { - val listByteLength = content.readInt() - val positionInParcel = content.readInt() - - content.setDataPosition(content.dataPosition() + positionInParcel) - content.setDataSize(content.dataPosition() + listByteLength) - } else { - free() - } - } - - private var outputTimestamp = 0L - private var outputEventType = 0 - private var outputInstanceId = 0 - private var outputPackageName = DUMMY_STRING - private var outputClassName = DUMMY_STRING - - override val timestamp get() = outputTimestamp - override val eventType get() = outputEventType - override val instanceId get() = outputInstanceId - override val packageName get() = outputPackageName - override val className get() = outputClassName - - override fun loadNextEvent(): Boolean { - if (free) return false - if (strings == null) throw IllegalStateException() - - val packageIndex = content.readInt() - val classIndex = content.readInt() - val instanceId = content.readInt() - val taskRootPackageIndex = content.readInt() - val taskRootClassIndex = content.readInt() - val eventType = content.readInt() - val timestamp = content.readLong() - - when (eventType) { - CONFIGURATION_CHANGE -> { - val newConfiguration = Configuration.CREATOR.createFromParcel(content) - } - SHORTCUT_INVOCATION -> { - val shortcutId = content.readString() - } - CHOOSER_ACTION -> { - val action = content.readString() - val contentType = content.readString() - val contentAnnotations = content.createStringArray() - } - STANDBY_BUCKET_CHANGED -> { - val bucketAndReason = content.readInt() - } - NOTIFICATION_INTERRUPTION -> { - val notificationChannelId = content.readString() - } - LOCUS_ID_SET -> { - val locusId = content.readString() - } - } - val flags = content.readInt() - - outputTimestamp = timestamp - outputEventType = eventType - outputInstanceId = instanceId - outputPackageName = if (packageIndex == -1) DUMMY_STRING else strings[packageIndex] - outputClassName = if(classIndex == -1) DUMMY_STRING else strings[classIndex] - - index++; if (index == length) free() - - return true - } - - override fun free() { - if (!free) { - content.recycle() - - free = true - } - } - - class UntestedSystemVersionException: RuntimeException("untested system version for the Parcel implementation") -} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseExperimentalFlagFragment.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseExperimentalFlagFragment.kt index dfbd1cf..c276200 100644 --- a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseExperimentalFlagFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseExperimentalFlagFragment.kt @@ -201,12 +201,6 @@ data class DiagnoseExperimentalFlagItem( enableFlags = ExperimentalFlags.INSTANCE_ID_FG_APP_DETECTION, disableFlags = ExperimentalFlags.INSTANCE_ID_FG_APP_DETECTION, enable = { true } - ), - DiagnoseExperimentalFlagItem( - label = R.string.diagnose_exf_fda, - enableFlags = ExperimentalFlags.DISABLE_FG_APP_DETECTION_FALLBACK, - disableFlags = ExperimentalFlags.DISABLE_FG_APP_DETECTION_FALLBACK, - enable = { true } ) ) } diff --git a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseForegroundAppFragment.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseForegroundAppFragment.kt index 9142d26..d4a5f92 100644 --- a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseForegroundAppFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseForegroundAppFragment.kt @@ -16,14 +16,11 @@ package io.timelimit.android.ui.diagnose import android.app.Activity -import android.app.usage.UsageEvents import android.app.usage.UsageStatsManager import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle -import android.os.Parcel -import android.util.Base64 import android.util.JsonWriter import android.util.Log import android.view.LayoutInflater @@ -38,7 +35,7 @@ import io.timelimit.android.R import io.timelimit.android.async.Threads import io.timelimit.android.databinding.DiagnoseForegroundAppFragmentBinding import io.timelimit.android.integration.platform.android.foregroundapp.InstanceIdForegroundAppHelper -import io.timelimit.android.integration.platform.android.foregroundapp.usagestats.ParcelUsageStatsReader +import io.timelimit.android.integration.platform.android.foregroundapp.usagestats.DirectUsageStatsReader import io.timelimit.android.livedata.liveDataFromNonNullValue import io.timelimit.android.livedata.liveDataFromNullableValue import io.timelimit.android.livedata.map @@ -53,7 +50,6 @@ import java.io.OutputStreamWriter class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle { companion object { private const val LOG_TAG = "DiagnoseForegroundApp" - private const val REQ_EXPORT_BINARY = 1 private const val REQ_EXPORT_TEXT = 2 private val buttonIntervals = listOf( @@ -144,71 +140,13 @@ class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle { } } - binding.osUsageStatsBinaryExportButton.isEnabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - binding.osUsageStatsBinaryExportButton.setOnClickListener { - if (auth.requestAuthenticationOrReturnTrue()) { - try { - startActivityForResult( - Intent(Intent.ACTION_CREATE_DOCUMENT) - .addCategory(Intent.CATEGORY_OPENABLE) - .setType("application/octet-stream") - .putExtra(Intent.EXTRA_TITLE, "timelimit-usage-stats-export.bin"), - REQ_EXPORT_BINARY - ) - } catch (ex: Exception) { - Toast.makeText(context, R.string.error_general, Toast.LENGTH_SHORT).show() - } - } - } - return binding.root } override fun getCustomTitle(): LiveData = liveDataFromNullableValue("${getString(R.string.diagnose_fga_title)} < ${getString(R.string.about_diagnose_title)} < ${getString(R.string.main_tab_overview)}") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQ_EXPORT_BINARY) { - if (resultCode == Activity.RESULT_OK) { - val context = requireContext().applicationContext - - Thread { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - throw RuntimeException("unsupported os version") - } - - try { - val now = System.currentTimeMillis() - val service = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - val currentData = service.queryEvents(now - InstanceIdForegroundAppHelper.START_QUERY_INTERVAL, now) - - val bytes = try { - val parcel = ParcelUsageStatsReader.getParcel(currentData) - try { parcel.marshall() } finally { parcel.recycle() } - } finally { - val event = UsageEvents.Event() - - while (currentData.getNextEvent(event)) {/* consume all items */} - } - - context.contentResolver.openOutputStream(data!!.data!!)!!.use { stream -> - stream.write(bytes) - } - - Threads.mainThreadHandler.post { - Toast.makeText(context, R.string.diagnose_fga_export_toast_done, Toast.LENGTH_SHORT).show() - } - } catch (ex: Exception) { - if (BuildConfig.DEBUG) { - Log.w(LOG_TAG, "could not do export", ex) - } - - Threads.mainThreadHandler.post { - Toast.makeText(context, R.string.error_general, Toast.LENGTH_SHORT).show() - } - } - }.start() - } - } else if (requestCode == REQ_EXPORT_TEXT) { + if (requestCode == REQ_EXPORT_TEXT) { if (resultCode == Activity.RESULT_OK) { val context = requireContext().applicationContext @@ -221,115 +159,41 @@ class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle { val now = System.currentTimeMillis() val service = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager val currentData = service.queryEvents(now - InstanceIdForegroundAppHelper.START_QUERY_INTERVAL, now) - val event = UsageEvents.Event() + val reader = DirectUsageStatsReader(currentData) try { - var nativeData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val parcel = ParcelUsageStatsReader.getParcel(currentData) - val bytes = parcel.marshall() - val reader = ParcelUsageStatsReader(parcel) - - NativeEventReader(reader, bytes, parcel) - } else null - - try { - JsonWriter( - BufferedWriter( - OutputStreamWriter( - context.contentResolver.openOutputStream( - data!!.data!! - )!! - ) + JsonWriter( + BufferedWriter( + OutputStreamWriter( + context.contentResolver.openOutputStream( + data!!.data!! + )!! ) - ).use { writer -> - writer.setIndent(" ") + ) + ).use { writer -> + writer.setIndent(" ") + + writer.beginArray() + + while (reader.loadNextEvent()) { writer.beginObject() - nativeData?.let { native -> - writer.name("header").value(Base64.encodeToString( - native.bytes, 0, native.parcel.dataPosition(), Base64.NO_WRAP or Base64.NO_PADDING - )) - } + writer.name("timestamp").value(reader.timestamp) + writer.name("type").value(reader.eventType) + writer.name("packageName").value(reader.packageName) + writer.name("className").value(reader.className) - writer.name("events").also { - writer.beginArray() + val instanceId = try { reader.instanceId } catch (ex: Exception) { null } - while (currentData.getNextEvent(event)) { - writer.beginObject() - - writer.name("timestamp").value(event.timeStamp) - writer.name("type").value(event.eventType) - writer.name("packageName").value(event.packageName) - writer.name("className").value(event.className) - - nativeData?.let { native -> - val positionBefore = native.parcel.dataPosition() - - val result: ReadNextItemResult = try { - if (native.events.loadNextEvent()) ReadNextItemResult.NextItem - else ReadNextItemResult.EndOfData - } catch (ex: Exception) { - ReadNextItemResult.Error(ex) - } - - val positionAfter = native.parcel.dataPosition().let { - if (it < positionBefore) native.bytes.size else it - } - - writer.name("custom") - - when (result) { - ReadNextItemResult.NextItem -> { - writer.beginObject() - - writer.name("timestamp").value(native.events.timestamp) - writer.name("type").value(native.events.eventType) - writer.name("instanceId").value(native.events.instanceId) - writer.name("packageName").value(native.events.packageName) - writer.name("className").value(native.events.className) - writer.name("binary").value(Base64.encodeToString( - native.bytes, positionBefore, positionAfter - positionBefore, - Base64.NO_WRAP or Base64.NO_PADDING - )) - - writer.endObject() - } - ReadNextItemResult.EndOfData -> { - writer.value("end of data reached") - - native.free() - nativeData = null - } - is ReadNextItemResult.Error -> { - writer.beginObject() - - writer.name("error message").value("${result.err}") - writer.name("remaining binary data").value(Base64.encodeToString( - native.bytes, positionBefore, native.bytes.size - positionBefore, - Base64.NO_WRAP or Base64.NO_PADDING - )) - - writer.endObject() - - native.free() - nativeData = null - } - } - } - - writer.endObject() - } - - writer.endArray() - } + if (instanceId != null) writer.name("instanceId").value(instanceId) writer.endObject() } - } finally { - nativeData?.free() + + writer.endArray() } } finally { - while (currentData.getNextEvent(event)) {/* consume all items */} + reader.free() } Threads.mainThreadHandler.post { @@ -348,14 +212,4 @@ class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle { } } else super.onActivityResult(requestCode, resultCode, data) } - - internal class NativeEventReader(val events: ParcelUsageStatsReader, val bytes: ByteArray, val parcel: Parcel /* owned by TlUsageEvents */) { - fun free() { events.free() } - } - - sealed class ReadNextItemResult { - object EndOfData: ReadNextItemResult() - object NextItem: ReadNextItemResult() - class Error(val err: Exception): ReadNextItemResult() - } } diff --git a/app/src/main/res/layout/diagnose_foreground_app_fragment.xml b/app/src/main/res/layout/diagnose_foreground_app_fragment.xml index 5f47b51..3dc2042 100644 --- a/app/src/main/res/layout/diagnose_foreground_app_fragment.xml +++ b/app/src/main/res/layout/diagnose_foreground_app_fragment.xml @@ -71,13 +71,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> -