From d9df1ec39e295b6fcd630881872c7124a7ca0dad Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Thu, 27 Jun 2019 16:10:45 -0400 Subject: [PATCH] Preparing to target API 28. --- AndroidManifest.xml | 3 + .../logsubmit/SubmitLogFragment.java | 70 +++++++++++--- .../securesms/util/BucketInfo.java | 95 +++++++++++++++++++ 3 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/util/BucketInfo.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 8250b616b2..d213a5dab5 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -58,6 +58,7 @@ + @@ -718,6 +719,8 @@ + + diff --git a/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java b/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java index 6a972ed175..76a96c1586 100644 --- a/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java +++ b/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java @@ -21,6 +21,7 @@ import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; +import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -31,10 +32,6 @@ import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import android.text.ClipboardManager; import android.text.TextUtils; import android.text.method.LinkMovementMethod; @@ -48,6 +45,12 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import org.json.JSONException; import org.json.JSONObject; import org.thoughtcrime.securesms.ApplicationContext; @@ -55,6 +58,7 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logsubmit.util.Scrubber; +import org.thoughtcrime.securesms.util.BucketInfo; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; @@ -67,6 +71,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.Locale; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import okhttp3.MediaType; import okhttp3.MultipartBody; @@ -93,6 +98,7 @@ public class SubmitLogFragment extends Fragment { private static final String HEADER_SYSINFO = "========== SYSINFO ========"; private static final String HEADER_JOBS = "=========== JOBS ========="; + private static final String HEADER_POWER = "========== POWER ========="; private static final String HEADER_LOGCAT = "========== LOGCAT ========"; private static final String HEADER_LOGGER = "========== LOGGER ========"; @@ -371,14 +377,34 @@ public class SubmitLogFragment extends Fragment { String scrubbedLogcat = scrubber.scrub(logcat); Log.i(TAG, "Scrub logcat: " + (System.currentTimeMillis() - t4) + " ms"); - return HEADER_SYSINFO + "\n\n" + - buildDescription(context) + "\n\n\n" + - HEADER_JOBS + "\n\n" + - scrubber.scrub(ApplicationContext.getInstance(context).getJobManager().getDebugInfo()) + "\n\n" + - HEADER_LOGCAT + "\n\n" + - scrubbedLogcat + "\n\n\n" + - HEADER_LOGGER + "\n\n" + - newLogs; + + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(HEADER_SYSINFO) + .append("\n\n") + .append(buildDescription(context)) + .append("\n\n\n") + .append(HEADER_JOBS) + .append("\n\n") + .append(scrubber.scrub(ApplicationContext.getInstance(context).getJobManager().getDebugInfo())) + .append("\n\n\n"); + + if (VERSION.SDK_INT >= 22) { + stringBuilder.append(HEADER_POWER) + .append("\n\n") + .append(buildPower(context)) + .append("\n\n\n"); + } + + stringBuilder.append(HEADER_LOGCAT) + .append("\n\n") + .append(scrubbedLogcat) + .append("\n\n\n") + .append(HEADER_LOGGER) + .append("\n\n") + .append(newLogs); + + return stringBuilder.toString(); } @Override @@ -485,7 +511,7 @@ public class SubmitLogFragment extends Fragment { return activityManager.getMemoryClass() + lowMem; } - private static String buildDescription(Context context) { + private static CharSequence buildDescription(Context context) { final PackageManager pm = context.getPackageManager(); final StringBuilder builder = new StringBuilder(); @@ -513,7 +539,23 @@ public class SubmitLogFragment extends Fragment { builder.append("Unknown\n"); } - return builder.toString(); + return builder; + } + + @RequiresApi(api = 22) + private static CharSequence buildPower(@NonNull Context context) { + final UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); + + if (usageStatsManager == null) { + return "UsageStatsManager not available"; + } + + BucketInfo info = BucketInfo.getInfo(usageStatsManager, TimeUnit.DAYS.toMillis(3)); + + return new StringBuilder().append("Current bucket: ").append(BucketInfo.bucketToString(info.getCurrentBucket())).append('\n') + .append("Highest bucket: ").append(BucketInfo.bucketToString(info.getBestBucket())).append('\n') + .append("Lowest bucket : ").append(BucketInfo.bucketToString(info.getWorstBucket())).append("\n\n") + .append(info.getHistory()); } private static Iterable getSupportedAbis() { diff --git a/src/org/thoughtcrime/securesms/util/BucketInfo.java b/src/org/thoughtcrime/securesms/util/BucketInfo.java new file mode 100644 index 0000000000..58a3181713 --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/BucketInfo.java @@ -0,0 +1,95 @@ +package org.thoughtcrime.securesms.util; + +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManager; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import java.util.Date; + +@RequiresApi(api = 22) +public final class BucketInfo { + + /** + * UsageStatsManager.STANDBY_BUCKET_EXEMPTED: is a Hidden API + */ + public static final int STANDBY_BUCKET_EXEMPTED = 5; + + private final int currentBucket; + private final int worstBucket; + private final int bestBucket; + private final CharSequence history; + + private BucketInfo(int currentBucket, int worstBucket, int bestBucket, CharSequence history) { + this.currentBucket = currentBucket; + this.worstBucket = worstBucket; + this.bestBucket = bestBucket; + this.history = history; + } + + public static @NonNull BucketInfo getInfo(@NonNull UsageStatsManager usageStatsManager, long overLastDurationMs) { + StringBuilder stringBuilder = new StringBuilder(); + + int currentBucket = usageStatsManager.getAppStandbyBucket(); + int worseBucket = currentBucket; + int bestBucket = currentBucket; + + long now = System.currentTimeMillis(); + UsageEvents.Event event = new UsageEvents.Event(); + UsageEvents usageEvents = usageStatsManager.queryEventsForSelf(now - overLastDurationMs, now); + + while (usageEvents.hasNextEvent()) { + usageEvents.getNextEvent(event); + + if (event.getEventType() == UsageEvents.Event.STANDBY_BUCKET_CHANGED) { + int appStandbyBucket = event.getAppStandbyBucket(); + + stringBuilder.append(new Date(event.getTimeStamp())) + .append(": ") + .append("Bucket Change: ") + .append(bucketToString(appStandbyBucket)) + .append("\n"); + + if (appStandbyBucket > worseBucket) { + worseBucket = appStandbyBucket; + } + if (appStandbyBucket < bestBucket) { + bestBucket = appStandbyBucket; + } + } + } + + return new BucketInfo(currentBucket, worseBucket, bestBucket, stringBuilder); + } + + /** + * Not localized, for logs and debug only. + */ + public static String bucketToString(int bucket) { + switch (bucket) { + case UsageStatsManager.STANDBY_BUCKET_ACTIVE: return "Active"; + case UsageStatsManager.STANDBY_BUCKET_FREQUENT: return "Frequent"; + case UsageStatsManager.STANDBY_BUCKET_WORKING_SET: return "Working Set"; + case UsageStatsManager.STANDBY_BUCKET_RARE: return "Rare"; + case STANDBY_BUCKET_EXEMPTED: return "Exempted"; + default: return "Unknown " + bucket; + } + } + + public int getBestBucket() { + return bestBucket; + } + + public int getWorstBucket() { + return worstBucket; + } + + public int getCurrentBucket() { + return currentBucket; + } + + public CharSequence getHistory() { + return history; + } +}