diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a2de6ba9..ebdd94276 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -68,7 +68,7 @@ material3-adaptive-navigation-suite = "1.4.0" media3 = "1.8.0" media3Ui = "1.8.0" # @keep -minSdk = "35" +minSdk = "36" okHttp = "5.2.1" playServicesWearable = "19.0.0" protobuf = "4.32.1" diff --git a/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerJavaSnippets.java b/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerJavaSnippets.java index 4dae4d353..b269faad6 100644 --- a/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerJavaSnippets.java +++ b/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerJavaSnippets.java @@ -2,19 +2,30 @@ import android.app.Activity; import android.os.Bundle; +import android.os.ProfilingManager; +import android.os.ProfilingTrigger; import android.util.Log; +import java.util.List; +import java.util.ArrayList; import java.util.function.Consumer; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import android.os.ProfilingResult; import java.util.concurrent.Executors; import android.os.CancellationSignal; +import android.view.Choreographer; import androidx.tracing.Trace; import androidx.core.os.Profiling; import androidx.core.os.SystemTraceRequestBuilder; import androidx.core.os.BufferFillPolicy; +import com.example.snippets.R; public class ProfilingManagerJavaSnippets { public class MainActivityJava extends Activity { + // [START android_profiling_manager_anr_case_study_java_snippet_2] + private static final int NETWORK_TIMEOUT_MILLISECS = 2000; + // [END android_profiling_manager_anr_case_study_java_snippet_2] + public static final String TAG = "ProfilingManager"; @Override public void onCreate(Bundle savedInstanceState) { @@ -69,5 +80,117 @@ public void accept(ProfilingResult profilingResult) { stopSignal.cancel(); } // [END android_profiling_manager_record_system_trace_java] + + // [START android_profiling_manager_triggered_trace_java] + public void recordWithTrigger() { + ProfilingManager profilingManager = getApplicationContext().getSystemService( + ProfilingManager.class); + List triggers = new ArrayList<>(); + ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( + ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); + triggerBuilder.setRateLimitingPeriodHours(1); + triggers.add(triggerBuilder.build()); + + Executor mainExecutor = Executors.newSingleThreadExecutor(); + Consumer resultCallback = + new Consumer() { + @Override + public void accept(ProfilingResult profilingResult) { + // ... + if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { + Log.d(TAG, + "Received profiling result. file: " + profilingResult.getResultFilePath()); + } + } + }; + profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback); + profilingManager.addProfilingTriggers(triggers); + + Choreographer.getInstance().postFrameCallback((f) -> { + // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. + reportFullyDrawn(); + }); + } + // [END android_profiling_manager_triggered_trace_java] + + // [START android_profiling_manager_anr_case_study_java_snippet_1] + public void addANRTrigger() { + ProfilingManager profilingManager = getApplicationContext().getSystemService(ProfilingManager.class); + List triggers = new ArrayList<>(); + ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANR); + triggers.add(triggerBuilder.build()); + Executor mainExecutor = Executors.newSingleThreadExecutor(); + Consumer resultCallback = + profilingResult -> { + // Handle uploading trace to your back-end + }; + profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback); + profilingManager.addProfilingTriggers(triggers); + } + // [END android_profiling_manager_anr_case_study_java_snippet_1] + + // [START android_profiling_manager_anr_case_study_java_snippet_2] + public void setupButtonCallback() { + findViewById(R.id.submit).setOnClickListener(submitButtonView -> { + Trace.beginSection("MyApp:SubmitButton"); + onClickSubmit(); + Trace.endSection(); + }); + } + + public void onClickSubmit() { + prepareNetworkRequest(); + + boolean networkRequestSuccess = false; + int maxAttempts = 10; + while (!networkRequestSuccess && maxAttempts > 0) { + networkRequestSuccess = performNetworkRequest(NETWORK_TIMEOUT_MILLISECS); + maxAttempts--; + } + + if (networkRequestSuccess) { + handleNetworkResponse(); + } + } + + boolean performNetworkRequest(int timeoutMiliseconds) { + // [START_EXCLUDE] + cpuIntensiveComputation(20); + try { + if (Math.random() < 0.2) { + // Simulate performing a network request by waiting a random period of time + int networkRequestTimeMs = (int)(Math.random() * timeoutMiliseconds); + Thread.sleep(networkRequestTimeMs); + return true; + } else { + // Simulate a timeout + Thread.sleep(timeoutMiliseconds); + } + } catch (InterruptedException e) {} + return false; + // [END_EXCLUDE] + } + + // [START_EXCLUDE silent] + void cpuIntensiveComputation(int durationMs) { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < durationMs) {} + } + // [END_EXCLUDE silent] + + void prepareNetworkRequest() { + // [START_EXCLUDE] + cpuIntensiveComputation(1000); + // [END_EXCLUDE] + } + + public void handleNetworkResponse() { + Trace.beginSection("handleNetworkResponse"); + // [START_EXCLUDE] + cpuIntensiveComputation(2000); + // [END_EXCLUDE] + Trace.endSection(); + } + // [END android_profiling_manager_anr_case_study_java_snippet_2] } } diff --git a/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerKotlinSnippets.kt b/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerKotlinSnippets.kt index f3f0ad99c..775c5dd3e 100644 --- a/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerKotlinSnippets.kt +++ b/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerKotlinSnippets.kt @@ -20,20 +20,29 @@ import android.app.Activity import android.os.Build import android.os.Bundle import android.os.CancellationSignal +import android.os.ProfilingManager import android.os.ProfilingResult +import android.os.ProfilingTrigger import android.util.Log +import android.view.Choreographer import androidx.annotation.RequiresApi import androidx.core.os.BufferFillPolicy import androidx.core.os.SystemTraceRequestBuilder import androidx.core.os.requestProfiling import androidx.tracing.Trace +import java.util.ArrayList import java.util.concurrent.Executor +import java.util.concurrent.Executors import java.util.function.Consumer import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor class ProfilingManagerKotlinSnippets { class MainActivity : Activity() { + companion object { + const val TAG = "MyApp" + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sampleRecordSystemTrace() @@ -81,5 +90,34 @@ class ProfilingManagerKotlinSnippets { // Computations you want to profile } // [END android_profiling_manager_record_system_trace_kotlin] + + // [START android_profiling_manager_triggered_trace] + fun recordWithTrigger() { + val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) + + val triggers = ArrayList() + + val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) + .setRateLimitingPeriodHours(1) + + triggers.add(triggerBuilder.build()) + + val mainExecutor: Executor = Executors.newSingleThreadExecutor() + + val resultCallback = Consumer { profilingResult -> + if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { + Log.d(TAG, "Received profiling result. file: ${profilingResult.resultFilePath}") + } + } + + profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback) + profilingManager.addProfilingTriggers(triggers) + + Choreographer.getInstance().postFrameCallback { frameTimeNanos -> + // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. + reportFullyDrawn() + } + } + // [END android_profiling_manager_triggered_trace] } } diff --git a/misc/src/main/res/layout/activity_main.xml b/misc/src/main/res/layout/activity_main.xml index dbfa14759..1366cf438 100644 --- a/misc/src/main/res/layout/activity_main.xml +++ b/misc/src/main/res/layout/activity_main.xml @@ -40,4 +40,11 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> +