Merge "Show install type label for instant app"
diff --git a/Android.bp b/Android.bp
index 2c550fd..2f5c851 100644
--- a/Android.bp
+++ b/Android.bp
@@ -342,6 +342,14 @@
"staledataclass-annotation-processor",
"error_prone_android_framework",
],
+ // Exports needed for staledataclass-annotation-processor, see b/139342589.
+ javacflags: [
+ "-J--add-modules=jdk.compiler",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
required: [
// TODO(b/120066492): remove default_television.xml when the build system
// propagates "required" properly.
diff --git a/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java b/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java
new file mode 100644
index 0000000..b296148
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.opengl.perftests;
+
+import android.opengl.Matrix;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Random;
+
+@LargeTest
+public class MatrixPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Rule
+ public float[] array = new float[48];
+
+ @Rule
+ public float[] bigArray = new float[16 * 1024 * 1024];
+
+
+ @Test
+ public void testMultiplyMM() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 32, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMLeftOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 16, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMRightOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 0, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMAllOverlap() {
+ Random rng = new Random();
+ for (int i = 0; i < 16; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 0, array, 0, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMOutputBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(bigArray, 1024, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMAllBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ bigArray[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(bigArray, 1024, bigArray, 16, bigArray, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMV() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 20, array, 4, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVLeftOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 4, array, 4, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVRightOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 0, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVAllOverlap() {
+ Random rng = new Random();
+ for (int i = 0; i < 16; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 0, array, 0, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVOutputBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(bigArray, 1024, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVAllBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ bigArray[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(bigArray, 1024, bigArray, 16, bigArray, 0);
+ }
+ }
+
+ @Test
+ public void testTransposeM() {
+ Random rng = new Random();
+ for (int i = 0; i < 16; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.transposeM(array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testInvertM() {
+ // non-singular matrix
+ array[ 0] = 0.814f;
+ array[ 1] = 4.976f;
+ array[ 2] = -3.858f;
+ array[ 3] = 7.206f;
+ array[ 4] = 5.112f;
+ array[ 5] = -2.420f;
+ array[ 6] = 8.791f;
+ array[ 7] = 6.426f;
+ array[ 8] = 2.945f;
+ array[ 9] = 1.801f;
+ array[10] = -2.594f;
+ array[11] = 2.663f;
+ array[12] = -5.003f;
+ array[13] = -4.188f;
+ array[14] = 3.340f;
+ array[15] = -1.235f;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.invertM(array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testOrthoM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.orthoM(array, 0, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
+ }
+ }
+
+ @Test
+ public void testFrustumM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.frustumM(array, 0, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
+ }
+ }
+
+ @Test
+ public void testPerspectiveM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.perspectiveM(array, 0, 45.0f, 1.0f, 1.0f, 100.0f);
+ }
+ }
+
+ @Test
+ public void testLength() {
+ Random rng = new Random();
+ for (int i = 0; i < 3; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.length(array[0], array[1], array[2]);
+ }
+ }
+
+ @Test
+ public void testSetIdentityM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setIdentityM(array, 0);
+ }
+ }
+
+ @Test
+ public void testScaleM() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.scaleM(array, 19, array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testScaleMInPlace() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.scaleM(array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testTranslateM() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.translateM(array, 19, array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testTranslateMInPlace() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.translateM(array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testRotateM() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.rotateM(array, 20, array, 0, array[16], array[17], array[18], array[19]);
+ }
+ }
+
+ @Test
+ public void testRotateMInPlace() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.rotateM(array, 0, array[16], array[17], array[18], array[19]);
+ }
+ }
+
+ @Test
+ public void testSetRotateM() {
+ Random rng = new Random();
+ array[0] = rng.nextFloat() * 90.0f;
+ array[1] = rng.nextFloat() + 0.5f;
+ array[2] = rng.nextFloat() + 0.5f;
+ array[3] = rng.nextFloat() + 0.5f;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setRotateM(array, 4, array[0], array[1], array[2], array[3]);
+ }
+ }
+
+ @Test
+ public void testSetRotateEulerM() {
+ Random rng = new Random();
+ array[0] = rng.nextFloat() * 90.0f;
+ array[1] = rng.nextFloat() * 90.0f;
+ array[2] = rng.nextFloat() * 90.0f;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setRotateEulerM(array, 3, array[0], array[1], array[2]);
+ }
+ }
+
+ @Test
+ public void testSetLookAtM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setLookAtM(array, 9,
+ 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f);
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 1e13dbf..1775d90 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -882,6 +882,8 @@
// a user-initiated action, it should be fine to just
// put USER instead of UNINSTALL or DISABLED.
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+ /* includeSchedulingApp */ true,
+ /* includeSourceApp */ true,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_UNINSTALL,
"app disabled");
@@ -932,6 +934,7 @@
// get here, but since this is generally a user-initiated action, it should
// be fine to just put USER instead of UNINSTALL or DISABLED.
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+ /* includeSchedulingApp */ true, /* includeSourceApp */ true,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
for (int c = 0; c < mControllers.size(); ++c) {
@@ -986,7 +989,12 @@
Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
}
synchronized (mLock) {
+ // Exclude jobs scheduled on behalf of this app for now because SyncManager
+ // and other job proxy agents may not know to reschedule the job properly
+ // after force stop.
+ // TODO(209852664): determine how to best handle syncs & other proxied jobs
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+ /* includeSchedulingApp */ true, /* includeSourceApp */ false,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_CANCELED,
"app force stopped");
@@ -1304,16 +1312,18 @@
}
}
+ private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> {
+ // There's no guarantee that the process has been stopped by the time we get
+ // here, but since this is a user-initiated action, it should be fine to just
+ // put USER instead of UNINSTALL or DISABLED.
+ cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
+ JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
+ };
+
private void cancelJobsForUserLocked(int userHandle) {
- final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
- for (int i = 0; i < jobsForUser.size(); i++) {
- JobStatus toRemove = jobsForUser.get(i);
- // There's no guarantee that the process has been stopped by the time we get here,
- // but since this is a user-initiated action, it should be fine to just put USER
- // instead of UNINSTALL or DISABLED.
- cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
- JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
- }
+ mJobs.forEachJob(
+ (job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle,
+ mCancelJobDueToUserRemovalConsumer);
}
private void cancelJobsForNonExistentUsers() {
@@ -1324,15 +1334,31 @@
}
private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
+ boolean includeSchedulingApp, boolean includeSourceApp,
@JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
+ if (!includeSchedulingApp && !includeSourceApp) {
+ Slog.wtfStack(TAG,
+ "Didn't indicate whether to cancel jobs for scheduling and/or source app");
+ includeSourceApp = true;
+ }
if ("android".equals(pkgName)) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
return;
}
- final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
+ final List<JobStatus> jobsForUid = new ArrayList<>();
+ if (includeSchedulingApp) {
+ mJobs.getJobsByUid(uid, jobsForUid);
+ }
+ if (includeSourceApp) {
+ mJobs.getJobsBySourceUid(uid, jobsForUid);
+ }
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
final JobStatus job = jobsForUid.get(i);
- if (job.getSourcePackageName().equals(pkgName)) {
+ final boolean shouldCancel =
+ (includeSchedulingApp
+ && job.getServiceComponent().getPackageName().equals(pkgName))
+ || (includeSourceApp && job.getSourcePackageName().equals(pkgName));
+ if (shouldCancel) {
cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index fcfb45c..78ab06c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -22,6 +22,7 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.content.ComponentName;
@@ -32,7 +33,6 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
@@ -287,26 +287,37 @@
}
/**
- * @param userHandle User for whom we are querying the list of jobs.
- * @return A list of all the jobs scheduled for the provided user. Never null.
+ * @param sourceUid Uid of the source app.
+ * @return A list of all the jobs scheduled for the source app. Never null.
*/
- public List<JobStatus> getJobsByUser(int userHandle) {
- return mJobSet.getJobsByUser(userHandle);
+ @NonNull
+ public List<JobStatus> getJobsBySourceUid(int sourceUid) {
+ return mJobSet.getJobsBySourceUid(sourceUid);
+ }
+
+ public void getJobsBySourceUid(int sourceUid, @NonNull List<JobStatus> insertInto) {
+ mJobSet.getJobsBySourceUid(sourceUid, insertInto);
}
/**
* @param uid Uid of the requesting app.
* @return All JobStatus objects for a given uid from the master list. Never null.
*/
+ @NonNull
public List<JobStatus> getJobsByUid(int uid) {
return mJobSet.getJobsByUid(uid);
}
+ public void getJobsByUid(int uid, @NonNull List<JobStatus> insertInto) {
+ mJobSet.getJobsByUid(uid, insertInto);
+ }
+
/**
* @param uid Uid of the requesting app.
* @param jobId Job id, specified at schedule-time.
* @return the JobStatus that matches the provided uId and jobId, or null if none found.
*/
+ @Nullable
public JobStatus getJobByUidAndJobId(int uid, int jobId) {
return mJobSet.get(uid, jobId);
}
@@ -1220,27 +1231,31 @@
public List<JobStatus> getJobsByUid(int uid) {
ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>();
- ArraySet<JobStatus> jobs = mJobs.get(uid);
- if (jobs != null) {
- matchingJobs.addAll(jobs);
- }
+ getJobsByUid(uid, matchingJobs);
return matchingJobs;
}
- // By user, not by uid, so we need to traverse by key and check
- public List<JobStatus> getJobsByUser(int userId) {
- final ArrayList<JobStatus> result = new ArrayList<JobStatus>();
- for (int i = mJobsPerSourceUid.size() - 1; i >= 0; i--) {
- if (UserHandle.getUserId(mJobsPerSourceUid.keyAt(i)) == userId) {
- final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(i);
- if (jobs != null) {
- result.addAll(jobs);
- }
- }
+ public void getJobsByUid(int uid, List<JobStatus> insertInto) {
+ ArraySet<JobStatus> jobs = mJobs.get(uid);
+ if (jobs != null) {
+ insertInto.addAll(jobs);
}
+ }
+
+ @NonNull
+ public List<JobStatus> getJobsBySourceUid(int sourceUid) {
+ final ArrayList<JobStatus> result = new ArrayList<JobStatus>();
+ getJobsBySourceUid(sourceUid, result);
return result;
}
+ public void getJobsBySourceUid(int sourceUid, List<JobStatus> insertInto) {
+ final ArraySet<JobStatus> jobs = mJobsPerSourceUid.get(sourceUid);
+ if (jobs != null) {
+ insertInto.addAll(jobs);
+ }
+ }
+
public boolean add(JobStatus job) {
final int uid = job.getUid();
final int sourceUid = job.getSourceUid();
@@ -1382,7 +1397,7 @@
}
public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate,
- Consumer<JobStatus> functor) {
+ @NonNull Consumer<JobStatus> functor) {
for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) {
ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex);
if (jobs != null) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 9a76523..e30761f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18646,8 +18646,10 @@
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
field public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1; // 0x1
- field public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
+ field public static final int LIGHT_CAPABILITY_COLOR_RGB = 2; // 0x2
+ field @Deprecated public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
field public static final int LIGHT_TYPE_INPUT = 10001; // 0x2711
+ field public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003; // 0x2713
field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
field public static final int LIGHT_TYPE_PLAYER_ID = 10002; // 0x2712
}
@@ -22436,6 +22438,7 @@
field public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
field public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
+ field public static final String MIMETYPE_IMAGE_AVIF = "image/avif";
field public static final String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
field public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cf5f10b..7a9f3c1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6662,6 +6662,9 @@
// Pass the current context to HardwareRenderer
HardwareRenderer.setContextForInit(getSystemContext());
+ if (data.persistent) {
+ HardwareRenderer.setIsSystemOrPersistent();
+ }
// Instrumentation info affects the class loader, so load it before
// setting up the app context.
diff --git a/core/java/android/content/res/ResourceTimer.java b/core/java/android/content/res/ResourceTimer.java
index 13f0b72..d51f64c 100644
--- a/core/java/android/content/res/ResourceTimer.java
+++ b/core/java/android/content/res/ResourceTimer.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppProtoEnums;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -30,6 +31,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FrameworkStatsLog;
import java.io.FileOutputStream;
import java.io.PrintWriter;
@@ -111,6 +113,14 @@
private static Config sConfig;
/**
+ * This array contains the statsd enum associated with each timer entry. A value of NONE (0)
+ * means that the entry should not be logged to statsd. (This would be the case for timers
+ * that are created for temporary debugging.)
+ */
+ @GuardedBy("sLock")
+ private static int[] sApiMap;
+
+ /**
* A singleton Summary object that is refilled from the native side. The length of the array
* is the number of timers that can be fetched. nativeGetTimers() will fill the array to the
* smaller of the length of the array or the actual number of timers in the runtime. The
@@ -165,6 +175,19 @@
sTimers[i].percentile = new int[sConfig.maxBuckets];
sTimers[i].largest = new int[sConfig.maxLargest];
}
+ // Map the values returned from the runtime to statsd enumerals The runtime may
+ // return timers that are not meant to be logged via statsd. Such timers are mapped
+ // to RESOURCE_API_NONE.
+ sApiMap = new int[sConfig.maxTimer];
+ for (int i = 0; i < sApiMap.length; i++) {
+ if (sConfig.timers[i].equals("GetResourceValue")) {
+ sApiMap[i] = AppProtoEnums.RESOURCE_API_GET_VALUE;
+ } else if (sConfig.timers[i].equals("RetrieveAttributes")) {
+ sApiMap[i] = AppProtoEnums.RESOURCE_API_RETRIEVE_ATTRIBUTES;
+ } else {
+ sApiMap[i] = AppProtoEnums.RESOURCE_API_NONE;
+ }
+ }
sCurrentPoint = 0;
startTimer();
@@ -194,7 +217,9 @@
delay = sPublicationPoints[sCurrentPoint];
} else {
// Repeat with the final publication point.
- delay = sCurrentPoint * sPublicationPoints[sPublicationPoints.length - 1];
+ final long repeated = sPublicationPoints[sPublicationPoints.length - 1];
+ final int prelude = sPublicationPoints.length - 1;
+ delay = (sCurrentPoint - prelude) * repeated;
}
// Convert minutes to milliseconds.
delay *= 60 * 1000;
@@ -223,10 +248,19 @@
update(true);
// Log the number of records read. This happens a few times a day.
for (int i = 0; i < sTimers.length; i++) {
- if (sTimers[i].count > 0) {
+ var timer = sTimers[i];
+ if (timer.count > 0) {
Log.i(TAG, TextUtils.formatSimple("%s count=%d pvalues=%s",
- sConfig.timers[i], sTimers[i].count,
- packedString(sTimers[i].percentile)));
+ sConfig.timers[i], timer.count, packedString(timer.percentile)));
+ if (sApiMap[i] != AppProtoEnums.RESOURCE_API_NONE) {
+ FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_API_INFO,
+ sApiMap[i],
+ timer.count, timer.total,
+ timer.percentile[0], timer.percentile[1],
+ timer.percentile[2], timer.percentile[3],
+ timer.largest[0], timer.largest[1], timer.largest[2],
+ timer.largest[3], timer.largest[4]);
+ }
}
}
sCurrentPoint++;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 9e87037..90e92db 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -159,8 +159,9 @@
*
* <li> For a SurfaceView output surface, the timestamp base is {@link
* #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}. The timestamp is overridden with choreographer
- * pulses from the display subsystem for smoother display of camera frames. The timestamp
- * is roughly in the same time base as {@link android.os.SystemClock#uptimeMillis}.</li>
+ * pulses from the display subsystem for smoother display of camera frames when the camera
+ * device runs in fixed frame rate. The timestamp is roughly in the same time base as
+ * {@link android.os.SystemClock#uptimeMillis}.</li>
* <li> For an output surface of MediaRecorder, MediaCodec, or ImageReader with {@link
* android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usge flag, the timestamp base is
* {@link #TIMESTAMP_BASE_MONOTONIC}, which is roughly the same time base as
@@ -231,7 +232,8 @@
*
* <p>The timestamp of the output images are overridden with choreographer pulses from the
* display subsystem for smoother display of camera frames. An output target of SurfaceView
- * uses this time base by default.</p>
+ * uses this time base by default. Note that the timestamp override is done for fixed camera
+ * frame rate only.</p>
*
* <p>This timestamp base isn't applicable to SurfaceTexture targets. SurfaceTexture's
* {@link android.graphics.SurfaceTexture#updateTexImage updateTexImage} function always
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index c311379..1df9b75 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -60,15 +60,29 @@
public static final int LIGHT_TYPE_PLAYER_ID = 10002;
/**
+ * Type for lights that illuminate keyboard keys.
+ */
+ public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003;
+
+ /**
* Capability for lights that could adjust its LED brightness. If the capability is not present
- * the led can only be turned either on or off.
+ * the LED can only be turned either on or off.
*/
public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1 << 0;
/**
- * Capability for lights that has red, green and blue LEDs to control the light's color.
+ * Capability for lights that have red, green and blue LEDs to control the light's color.
*/
- public static final int LIGHT_CAPABILITY_RGB = 0 << 1;
+ public static final int LIGHT_CAPABILITY_COLOR_RGB = 1 << 1;
+
+ /**
+ * Capability for lights that have red, green and blue LEDs to control the light's color.
+ *
+ * @deprecated Wrong int based flag with value 0. Use capability flag {@code
+ * LIGHT_CAPABILITY_COLOR_RGB} instead.
+ */
+ @Deprecated
+ public static final int LIGHT_CAPABILITY_RGB = 0;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -77,6 +91,7 @@
LIGHT_TYPE_MICROPHONE,
LIGHT_TYPE_INPUT,
LIGHT_TYPE_PLAYER_ID,
+ LIGHT_TYPE_KEYBOARD_BACKLIGHT,
})
public @interface LightType {}
@@ -85,6 +100,7 @@
@IntDef(flag = true, prefix = {"LIGHT_CAPABILITY_"},
value = {
LIGHT_CAPABILITY_BRIGHTNESS,
+ LIGHT_CAPABILITY_COLOR_RGB,
LIGHT_CAPABILITY_RGB,
})
public @interface LightCapability {}
@@ -233,7 +249,7 @@
* @return True if the hardware can control the RGB led, otherwise false.
*/
public boolean hasRgbControl() {
- return (mCapabilities & LIGHT_CAPABILITY_RGB) == LIGHT_CAPABILITY_RGB;
+ return (mCapabilities & LIGHT_CAPABILITY_COLOR_RGB) == LIGHT_CAPABILITY_COLOR_RGB;
}
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8b3451e..92088e9 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -65,6 +65,7 @@
import android.annotation.UiContext;
import android.app.ActivityManager;
import android.app.Dialog;
+import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -546,6 +547,20 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // This is a bug id.
+ /**
+ * Disallow IMEs to override {@link InputMethodService#onCreateInputMethodSessionInterface()}
+ * method.
+ *
+ * <p>If IMEs targeting on Android U and beyond override the
+ * {@link InputMethodService#onCreateInputMethodSessionInterface()}, an {@link LinkageError}
+ * would be thrown.</p>
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private static final long DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE = 148086656L;
+
LayoutInflater mInflater;
TypedArray mThemeAttrs;
@UnsupportedAppUsage
@@ -1527,6 +1542,11 @@
}
@Override public void onCreate() {
+ if (methodIsOverridden("onCreateInputMethodSessionInterface")
+ && CompatChanges.isChangeEnabled(DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE)) {
+ throw new LinkageError("InputMethodService#onCreateInputMethodSessionInterface()"
+ + " can no longer be overridden!");
+ }
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate");
mTheme = Resources.selectSystemTheme(mTheme,
getApplicationInfo().targetSdkVersion,
@@ -1764,6 +1784,9 @@
* {@link InputMethodService#onDisplayCompletions(CompletionInfo[])},
* {@link InputMethodService#onUpdateExtractedText(int, ExtractedText)},
* {@link InputMethodService#onUpdateSelection(int, int, int, int, int, int)} instead.
+ *
+ * <p>IMEs targeting on Android U and above cannot override this method, or an
+ * {@link LinkageError} would be thrown.</p>
*/
@Deprecated
@Override
@@ -4067,4 +4090,13 @@
final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
}
+
+ private boolean methodIsOverridden(String methodName, Class<?>... parameterTypes) {
+ try {
+ return getClass().getMethod(methodName, parameterTypes).getDeclaringClass()
+ != InputMethodService.class;
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Method must exist.", e);
+ }
+ }
}
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
new file mode 100644
index 0000000..186b2a6
--- /dev/null
+++ b/core/java/android/service/credentials/Action.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * An action defined by the provider that intents into the provider's app for specific
+ * user actions.
+ *
+ * @hide
+ */
+public final class Action implements Parcelable {
+ /** Info to be displayed with this action on the UI. */
+ private final @NonNull Slice mInfo;
+ /**
+ * The pending intent to be invoked when the user selects this action.
+ */
+ private final @NonNull PendingIntent mPendingIntent;
+
+ /**
+ * Constructs an action to be displayed on the UI.
+ *
+ * @param actionInfo The info to be displayed along with this action.
+ * @param pendingIntent The intent to be invoked when the user selects this action.
+ * @throws NullPointerException If {@code actionInfo}, or {@code pendingIntent} is null.
+ */
+ public Action(@NonNull Slice actionInfo, @NonNull PendingIntent pendingIntent) {
+ Objects.requireNonNull(actionInfo, "actionInfo must not be null");
+ Objects.requireNonNull(pendingIntent, "pendingIntent must not be null");
+ mInfo = actionInfo;
+ mPendingIntent = pendingIntent;
+ }
+
+ private Action(@NonNull Parcel in) {
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ }
+
+ public static final @NonNull Creator<Action> CREATOR = new Creator<Action>() {
+ @Override
+ public Action createFromParcel(@NonNull Parcel in) {
+ return new Action(in);
+ }
+
+ @Override
+ public Action[] newArray(int size) {
+ return new Action[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Returns the action info as a {@link Slice} object, to be displayed on the UI.
+ */
+ public @NonNull Slice getActionInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Returns the {@link PendingIntent} to be invoked when the action is selected.
+ */
+ public @NonNull PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialCallback.java b/core/java/android/service/credentials/CreateCredentialCallback.java
new file mode 100644
index 0000000..6108eea
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialCallback.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Callback to be invoked as a response to {@link CreateCredentialRequest}.
+ *
+ * @hide
+ */
+public final class CreateCredentialCallback {
+ private static final String TAG = "CreateCredentialCallback";
+
+ private final ICreateCredentialCallback mCallback;
+
+ /** @hide */
+ public CreateCredentialCallback(@NonNull ICreateCredentialCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Invoked on a successful response for {@link CreateCredentialRequest}
+ * @param response The response from the credential provider.
+ */
+ public void onSuccess(@NonNull CreateCredentialResponse response) {
+ try {
+ mCallback.onSuccess(response);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Invoked on a failure response for {@link CreateCredentialRequest}
+ * @param errorCode The code defining the type of error.
+ * @param message The message corresponding to the failure.
+ */
+ public void onFailure(int errorCode, @Nullable CharSequence message) {
+ Log.w(TAG, "onFailure: " + message);
+ try {
+ mCallback.onFailure(errorCode, message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.aidl b/core/java/android/service/credentials/CreateCredentialRequest.aidl
new file mode 100644
index 0000000..eb7fba9
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialRequest.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable CreateCredentialRequest;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.java b/core/java/android/service/credentials/CreateCredentialRequest.java
new file mode 100644
index 0000000..ac11e04b
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialRequest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Request for creating a credential.
+ *
+ * @hide
+ */
+public final class CreateCredentialRequest implements Parcelable {
+ private final @NonNull String mCallingPackage;
+ private final @NonNull String mType;
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @throws IllegalArgumentException If {@code callingPackage}, or {@code type} string is
+ * null or empty.
+ * @throws NullPointerException If {@code data} is null.
+ */
+ public CreateCredentialRequest(@NonNull String callingPackage,
+ @NonNull String type, @NonNull Bundle data) {
+ mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage,
+ "callingPackage must not be null or empty");
+ mType = Preconditions.checkStringNotEmpty(type,
+ "type must not be null or empty");
+ mData = Objects.requireNonNull(data, "data must not be null");
+ }
+
+ private CreateCredentialRequest(@NonNull Parcel in) {
+ mCallingPackage = in.readString8();
+ mType = in.readString8();
+ mData = in.readBundle();
+ }
+
+ public static final @NonNull Creator<CreateCredentialRequest> CREATOR =
+ new Creator<CreateCredentialRequest>() {
+ @Override
+ public CreateCredentialRequest createFromParcel(@NonNull Parcel in) {
+ return new CreateCredentialRequest(in);
+ }
+
+ @Override
+ public CreateCredentialRequest[] newArray(int size) {
+ return new CreateCredentialRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mCallingPackage);
+ dest.writeString8(mType);
+ dest.writeBundle(mData);
+ }
+
+ /** Returns the calling package of the calling app. */
+ @NonNull
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /** Returns the type of the credential to be created. */
+ @NonNull
+ public String getType() {
+ return mType;
+ }
+
+ /** Returns the data to be used while creating the credential. */
+ @NonNull
+ public Bundle getData() {
+ return mData;
+ }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialResponse.aidl b/core/java/android/service/credentials/CreateCredentialResponse.aidl
new file mode 100644
index 0000000..73c9147
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+parcelable CreateCredentialResponse;
diff --git a/core/java/android/service/credentials/CreateCredentialResponse.java b/core/java/android/service/credentials/CreateCredentialResponse.java
new file mode 100644
index 0000000..f2ad7272
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialResponse.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Response to a {@link CreateCredentialRequest}.
+ *
+ * @hide
+ */
+public final class CreateCredentialResponse implements Parcelable {
+ private final @Nullable CharSequence mHeader;
+ private final @NonNull List<SaveEntry> mSaveEntries;
+
+ private CreateCredentialResponse(@NonNull Parcel in) {
+ mHeader = in.readCharSequence();
+ mSaveEntries = in.createTypedArrayList(SaveEntry.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeCharSequence(mHeader);
+ dest.writeTypedList(mSaveEntries);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<CreateCredentialResponse> CREATOR =
+ new Creator<CreateCredentialResponse>() {
+ @Override
+ public CreateCredentialResponse createFromParcel(@NonNull Parcel in) {
+ return new CreateCredentialResponse(in);
+ }
+
+ @Override
+ public CreateCredentialResponse[] newArray(int size) {
+ return new CreateCredentialResponse[size];
+ }
+ };
+
+ /* package-private */ CreateCredentialResponse(
+ @Nullable CharSequence header,
+ @NonNull List<SaveEntry> saveEntries) {
+ this.mHeader = header;
+ this.mSaveEntries = saveEntries;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSaveEntries);
+ }
+
+ /** Returns the header to be displayed on the UI. */
+ public @Nullable CharSequence getHeader() {
+ return mHeader;
+ }
+
+ /** Returns the list of save entries to be displayed on the UI. */
+ public @NonNull List<SaveEntry> getSaveEntries() {
+ return mSaveEntries;
+ }
+
+ /**
+ * A builder for {@link CreateCredentialResponse}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+
+ private @Nullable CharSequence mHeader;
+ private @NonNull List<SaveEntry> mSaveEntries = new ArrayList<>();
+
+ /** Sets the header to be displayed on the UI. */
+ public @NonNull Builder setHeader(@Nullable CharSequence header) {
+ mHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the list of save entries to be shown on the UI.
+ *
+ * @throws IllegalArgumentException If {@code saveEntries} is empty.
+ * @throws NullPointerException If {@code saveEntries} is null, or any of its elements
+ * are null.
+ */
+ public @NonNull Builder setSaveEntries(@NonNull List<SaveEntry> saveEntries) {
+ Preconditions.checkCollectionNotEmpty(saveEntries, "saveEntries");
+ mSaveEntries = Preconditions.checkCollectionElementsNotNull(
+ saveEntries, "saveEntries");
+ return this;
+ }
+
+ /**
+ * Adds an entry to the list of save entries to be shown on the UI.
+ *
+ * @throws NullPointerException If {@code saveEntry} is null.
+ */
+ public @NonNull Builder addSaveEntry(@NonNull SaveEntry saveEntry) {
+ mSaveEntries.add(Objects.requireNonNull(saveEntry));
+ return this;
+ }
+
+ /**
+ * Builds the instance.
+ *
+ * @throws IllegalArgumentException If {@code saveEntries} is empty.
+ */
+ public @NonNull CreateCredentialResponse build() {
+ Preconditions.checkCollectionNotEmpty(mSaveEntries, "saveEntries must "
+ + "not be empty");
+ return new CreateCredentialResponse(
+ mHeader,
+ mSaveEntries);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/Credential.java b/core/java/android/service/credentials/Credential.java
new file mode 100644
index 0000000..7d5da8a
--- /dev/null
+++ b/core/java/android/service/credentials/Credential.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import static java.util.Objects.requireNonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A Credential object that contains type specific data that is returned from the credential
+ * provider to the framework. Framework then converts it to an app facing representation and
+ * returns to the calling app.
+ *
+ * @hide
+ */
+public final class Credential implements Parcelable {
+ /** The type of this credential. */
+ private final @NonNull String mType;
+
+ /** The data associated with this credential. */
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a credential object.
+ *
+ * @param type The type of the credential.
+ * @param data The data of the credential that is passed back to the framework, and eventually
+ * to the calling app.
+ * @throws NullPointerException If {@code data} is null.
+ * @throws IllegalArgumentException If {@code type} is null or empty.
+ */
+ public Credential(@NonNull String type, @NonNull Bundle data) {
+ Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+ requireNonNull(data, "data must not be null");
+ this.mType = type;
+ this.mData = data;
+ }
+
+ private Credential(@NonNull Parcel in) {
+ mType = in.readString16NoHelper();
+ mData = in.readBundle();
+ }
+
+ /**
+ * Returns the type of the credential.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the data associated with the credential.
+ */
+ public @NonNull Bundle getData() {
+ return mData;
+ }
+
+ public static final @NonNull Creator<Credential> CREATOR = new Creator<Credential>() {
+ @Override
+ public Credential createFromParcel(@NonNull Parcel in) {
+ return new Credential(in);
+ }
+
+ @Override
+ public Credential[] newArray(int size) {
+ return new Credential[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mType);
+ dest.writeBundle(mData);
+ }
+}
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
new file mode 100644
index 0000000..b49215a
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A credential entry that is displayed on the account selector UI. Each entry corresponds to
+ * something that the user can select.
+ *
+ * @hide
+ */
+public final class CredentialEntry implements Parcelable {
+ /** The type of the credential entry to be shown on the UI. */
+ private final @NonNull String mType;
+
+ /** The info to be displayed along with this credential entry on the UI. */
+ private final @NonNull Slice mInfo;
+
+ /** The pending intent to be invoked when this credential entry is selected. */
+ private final @Nullable PendingIntent mPendingIntent;
+
+ /**
+ * The underlying credential to be returned to the app when the user selects
+ * this credential entry.
+ */
+ private final @Nullable Credential mCredential;
+
+ /** A flag denoting whether auto-select is enabled for this entry. */
+ private final @NonNull boolean mAutoSelectAllowed;
+
+ private CredentialEntry(@NonNull String type, @NonNull Slice entryInfo,
+ @Nullable PendingIntent pendingIntent, @Nullable Credential credential,
+ @NonNull boolean autoSeletAllowed) {
+ mType = type;
+ mInfo = entryInfo;
+ mPendingIntent = pendingIntent;
+ mCredential = credential;
+ mAutoSelectAllowed = autoSeletAllowed;
+ }
+
+ private CredentialEntry(@NonNull Parcel in) {
+ mType = in.readString();
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ mCredential = in.readParcelable(Credential.class.getClassLoader(),
+ Credential.class);
+ mAutoSelectAllowed = in.readBoolean();
+ }
+
+ public static final @NonNull Creator<CredentialEntry> CREATOR =
+ new Creator<CredentialEntry>() {
+ @Override
+ public CredentialEntry createFromParcel(@NonNull Parcel in) {
+ return new CredentialEntry(in);
+ }
+
+ @Override
+ public CredentialEntry[] newArray(int size) {
+ return new CredentialEntry[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mType);
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ mCredential.writeToParcel(dest, flags);
+ dest.writeBoolean(mAutoSelectAllowed);
+ }
+
+ /**
+ * Returns the specific credential type of the entry.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the UI info to be displayed for this entry.
+ */
+ public @NonNull Slice getInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Returns the pending intent to be invoked if the user selects this entry.
+ */
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ /**
+ * Returns the credential associated with this entry.
+ */
+ public @Nullable Credential getCredential() {
+ return mCredential;
+ }
+
+ /**
+ * Returns whether this entry can be auto selected if it is the only option for the user.
+ */
+ public @NonNull boolean isAutoSelectAllowed() {
+ return mAutoSelectAllowed;
+ }
+
+ /**
+ * Builder for {@link CredentialEntry}.
+ */
+ public static final class Builder {
+ private String mType;
+ private Slice mInfo;
+ private PendingIntent mPendingIntent;
+ private Credential mCredential;
+ private boolean mAutoSelectAllowed = false;
+
+ /**
+ * Builds the instance.
+ * @param type The type of credential underlying this credential entry.
+ * @param info The info to be displayed with this entry on the UI.
+ *
+ * @throws IllegalArgumentException If {@code type} is null or empty.
+ * @throws NullPointerException If {@code info} is null.
+ */
+ public Builder(@NonNull String type, @NonNull Slice info) {
+ mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+ + "null, or empty");
+ mInfo = Objects.requireNonNull(info, "info must not be null");
+ }
+
+ /**
+ * Sets the pendingIntent to be invoked if the user selects this entry.
+ *
+ * @throws IllegalStateException If {@code credential} is already set. Must either set the
+ * {@code credential}, or the {@code pendingIntent}.
+ */
+ public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+ Preconditions.checkState(pendingIntent != null && mCredential != null,
+ "credential is already set. Cannot set both the pendingIntent "
+ + "and the credential");
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the credential to be used, if the user selects this entry.
+ *
+ * @throws IllegalStateException If {@code pendingIntent} is already set. Must either set
+ * the {@code pendingIntent}, or the {@code credential}.
+ */
+ public @NonNull Builder setCredential(@Nullable Credential credential) {
+ Preconditions.checkState(credential != null && mPendingIntent != null,
+ "pendingIntent is already set. Cannot set both the "
+ + "pendingIntent and the credential");
+ mCredential = credential;
+ return this;
+ }
+
+ /**
+ * Sets whether the entry is allowed to be auto selected by the framework.
+ * The default value is set to false.
+ */
+ public @NonNull Builder setAutoSelectAllowed(@NonNull boolean autoSelectAllowed) {
+ mAutoSelectAllowed = autoSelectAllowed;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link CredentialEntry} instance.
+ *
+ * @throws NullPointerException If {@code info} is null.
+ * @throws IllegalArgumentException If {@code type} is null, or empty.
+ * @throws IllegalStateException If neither {@code pendingIntent} nor {@code credential}
+ * is set, or if both are set.
+ */
+ public @NonNull CredentialEntry build() {
+ Preconditions.checkState(mPendingIntent == null && mCredential == null,
+ "Either pendingIntent or credential must be set");
+ Preconditions.checkState(mPendingIntent != null && mCredential != null,
+ "Cannot set both the pendingIntent and credential");
+ return new CredentialEntry(mType, mInfo, mPendingIntent,
+ mCredential, mAutoSelectAllowed);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
new file mode 100644
index 0000000..1fe89df
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.app.Service;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Main service to be extended by credential providers, in order to return user credentials
+ * to the framework.
+ *
+ * @hide
+ */
+public abstract class CredentialProviderService extends Service {
+ private static final String TAG = "CredProviderService";
+ private Handler mHandler;
+
+ public static final String SERVICE_INTERFACE =
+ "android.service.credentials.CredentialProviderService";
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ public final @NonNull IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.i(TAG, "Failed to bind with intent: " + intent);
+ return null;
+ }
+
+ private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
+ @Override
+ public void onGetCredentials(GetCredentialsRequest request, ICancellationSignal transport,
+ IGetCredentialsCallback callback) throws RemoteException {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(transport);
+ Objects.requireNonNull(callback);
+
+ mHandler.sendMessage(obtainMessage(
+ CredentialProviderService::onGetCredentials,
+ CredentialProviderService.this, request,
+ CancellationSignal.fromTransport(transport),
+ new GetCredentialsCallback(callback)
+ ));
+ }
+
+ @Override
+ public void onCreateCredential(CreateCredentialRequest request,
+ ICancellationSignal transport, ICreateCredentialCallback callback)
+ throws RemoteException {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(transport);
+ Objects.requireNonNull(callback);
+
+ mHandler.sendMessage(obtainMessage(
+ CredentialProviderService::onCreateCredential,
+ CredentialProviderService.this, request,
+ CancellationSignal.fromTransport(transport),
+ new CreateCredentialCallback(callback)
+ ));
+ }
+ };
+
+ /**
+ * Called by the android system to retrieve user credentials from the connected provider
+ * service.
+ * @param request The credential request for the provider to handle.
+ * @param cancellationSignal Signal for providers to listen to any cancellation requests from
+ * the android system.
+ * @param callback Object used to relay the response of the credentials request.
+ */
+ public abstract void onGetCredentials(@NonNull GetCredentialsRequest request,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull GetCredentialsCallback callback);
+
+ /**
+ * Called by the android system to create a credential.
+ * @param request The credential creation request for the provider to handle.
+ * @param cancellationSignal Signal for providers to listen to any cancellation requests from
+ * the android system.
+ * @param callback Object used to relay the response of the credential creation request.
+ */
+ public abstract void onCreateCredential(@NonNull CreateCredentialRequest request,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull CreateCredentialCallback callback);
+}
diff --git a/core/java/android/service/credentials/CredentialsDisplayContent.java b/core/java/android/service/credentials/CredentialsDisplayContent.java
new file mode 100644
index 0000000..106f322
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialsDisplayContent.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Content to be displayed on the account selector UI, including credential entries,
+ * actions etc.
+ *
+ * @hide
+ */
+public final class CredentialsDisplayContent implements Parcelable {
+ /** Header to be displayed on the UI. */
+ private final @Nullable CharSequence mHeader;
+
+ /** List of credential entries to be displayed on the UI. */
+ private final @NonNull List<CredentialEntry> mCredentialEntries;
+
+ /** List of provider actions to be displayed on the UI. */
+ private final @NonNull List<Action> mActions;
+
+ private CredentialsDisplayContent(@Nullable CharSequence header,
+ @NonNull List<CredentialEntry> credentialEntries,
+ @NonNull List<Action> actions) {
+ mHeader = header;
+ mCredentialEntries = credentialEntries;
+ mActions = actions;
+ }
+
+ private CredentialsDisplayContent(@NonNull Parcel in) {
+ mHeader = in.readCharSequence();
+ mCredentialEntries = in.createTypedArrayList(CredentialEntry.CREATOR);
+ mActions = in.createTypedArrayList(Action.CREATOR);
+ }
+
+ public static final @NonNull Creator<CredentialsDisplayContent> CREATOR =
+ new Creator<CredentialsDisplayContent>() {
+ @Override
+ public CredentialsDisplayContent createFromParcel(@NonNull Parcel in) {
+ return new CredentialsDisplayContent(in);
+ }
+
+ @Override
+ public CredentialsDisplayContent[] newArray(int size) {
+ return new CredentialsDisplayContent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeCharSequence(mHeader);
+ dest.writeTypedList(mCredentialEntries);
+ dest.writeTypedList(mActions);
+ }
+
+ /**
+ * Returns the header to be displayed on the UI.
+ */
+ public @Nullable CharSequence getHeader() {
+ return mHeader;
+ }
+
+ /**
+ * Returns the list of credential entries to be displayed on the UI.
+ */
+ public @NonNull List<CredentialEntry> getCredentialEntries() {
+ return mCredentialEntries;
+ }
+
+ /**
+ * Returns the list of actions to be displayed on the UI.
+ */
+ public @NonNull List<Action> getActions() {
+ return mActions;
+ }
+
+ /**
+ * Builds an instance of {@link CredentialsDisplayContent}.
+ */
+ public static final class Builder {
+ private CharSequence mHeader = null;
+ private List<CredentialEntry> mCredentialEntries = new ArrayList<>();
+ private List<Action> mActions = new ArrayList<>();
+
+ /**
+ * Sets the header to be displayed on the UI.
+ */
+ public @NonNull Builder setHeader(@Nullable CharSequence header) {
+ mHeader = header;
+ return this;
+ }
+
+ /**
+ * Adds a {@link CredentialEntry} to the list of entries to be displayed on
+ * the UI.
+ *
+ * @throws NullPointerException If the {@code credentialEntry} is null.
+ */
+ public @NonNull Builder addCredentialEntry(@NonNull CredentialEntry credentialEntry) {
+ mCredentialEntries.add(Objects.requireNonNull(credentialEntry));
+ return this;
+ }
+
+ /**
+ * Adds an {@link Action} to the list of actions to be displayed on
+ * the UI.
+ *
+ * @throws NullPointerException If {@code action} is null.
+ */
+ public @NonNull Builder addAction(@NonNull Action action) {
+ mActions.add(Objects.requireNonNull(action, "action must not be null"));
+ return this;
+ }
+
+ /**
+ * Sets the list of actions to be displayed on the UI.
+ *
+ * @throws NullPointerException If {@code actions} is null, or any of its elements
+ * is null.
+ */
+ public @NonNull Builder setActions(@NonNull List<Action> actions) {
+ mActions = Preconditions.checkCollectionElementsNotNull(actions,
+ "actions");
+ return this;
+ }
+
+ /**
+ * Sets the list of credential entries to be displayed on the
+ * account selector UI.
+ *
+ * @throws NullPointerException If {@code credentialEntries} is null, or any of its
+ * elements is null.
+ */
+ public @NonNull Builder setCredentialEntries(
+ @NonNull List<CredentialEntry> credentialEntries) {
+ mCredentialEntries = Preconditions.checkCollectionElementsNotNull(
+ credentialEntries,
+ "credentialEntries");
+ return this;
+ }
+
+ /**
+ * Builds a {@link GetCredentialsResponse} instance.
+ *
+ * @throws NullPointerException If {@code credentialEntries} is null.
+ * @throws IllegalStateException if both {@code credentialEntries} and
+ * {@code actions} are empty.
+ */
+ public @NonNull CredentialsDisplayContent build() {
+ if (mCredentialEntries != null && mCredentialEntries.isEmpty()
+ && mActions != null && mActions.isEmpty()) {
+ throw new IllegalStateException("credentialEntries and actions must not both "
+ + "be empty");
+ }
+ return new CredentialsDisplayContent(mHeader, mCredentialEntries, mActions);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialOption.java b/core/java/android/service/credentials/GetCredentialOption.java
new file mode 100644
index 0000000..c6cda1d
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialOption.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A type specific credential request, containing the associated data to be used for
+ * retrieving credentials.
+ *
+ * @hide
+ */
+public final class GetCredentialOption implements Parcelable {
+ /** The type of credential requested. */
+ private final @NonNull String mType;
+
+ /** The data associated with the request. */
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a new instance of {@link GetCredentialOption}
+ *
+ * @throws IllegalArgumentException If {@code type} string is null or empty.
+ * @throws NullPointerException If {@code data} is null.
+ */
+ public GetCredentialOption(@NonNull String type, @NonNull Bundle data) {
+ Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+ requireNonNull(data, "data must not be null");
+ mType = type;
+ mData = data;
+ }
+
+ /**
+ * Returns the data associated with this credential request option.
+ */
+ public @NonNull Bundle getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the type associated with this credential request option.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ private GetCredentialOption(@NonNull Parcel in) {
+ mType = in.readString16NoHelper();
+ mData = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString16NoHelper(mType);
+ dest.writeBundle(mData);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<GetCredentialOption> CREATOR =
+ new Creator<GetCredentialOption>() {
+ @Override
+ public GetCredentialOption createFromParcel(@NonNull Parcel in) {
+ return new GetCredentialOption(in);
+ }
+
+ @Override
+ public GetCredentialOption[] newArray(int size) {
+ return new GetCredentialOption[size];
+ }
+ };
+}
diff --git a/core/java/android/service/credentials/GetCredentialsCallback.java b/core/java/android/service/credentials/GetCredentialsCallback.java
new file mode 100644
index 0000000..42a7394
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Callback to be invoked as a response to {@link GetCredentialsRequest}.
+ *
+ * @hide
+ */
+public final class GetCredentialsCallback {
+
+ private static final String TAG = "GetCredentialsCallback";
+
+ private final IGetCredentialsCallback mCallback;
+
+ /** @hide */
+ public GetCredentialsCallback(@NonNull IGetCredentialsCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Invoked on a successful response for {@link GetCredentialsRequest}
+ * @param response The response from the credential provider.
+ */
+ public void onSuccess(@NonNull GetCredentialsResponse response) {
+ try {
+ mCallback.onSuccess(response);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Invoked on a failure response for {@link GetCredentialsRequest}
+ * @param errorCode The code defining the kind of error.
+ * @param message The message corresponding to the failure.
+ */
+ public void onFailure(int errorCode, @Nullable CharSequence message) {
+ Log.w(TAG, "onFailure: " + message);
+ try {
+ mCallback.onFailure(errorCode, message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.aidl b/core/java/android/service/credentials/GetCredentialsRequest.aidl
new file mode 100644
index 0000000..b309d69
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsRequest;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.java b/core/java/android/service/credentials/GetCredentialsRequest.java
new file mode 100644
index 0000000..cf7c283
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Request for getting user's credentials from a given credential provider.
+ *
+ * @hide
+ */
+public final class GetCredentialsRequest implements Parcelable {
+ /** Calling package of the app requesting for credentials. */
+ private final @NonNull String mCallingPackage;
+
+ /**
+ * List of credential options. Each {@link GetCredentialOption} object holds parameters to
+ * be used for retrieving specific type of credentials.
+ */
+ private final @NonNull List<GetCredentialOption> mGetCredentialOptions;
+
+ private GetCredentialsRequest(@NonNull String callingPackage,
+ @NonNull List<GetCredentialOption> getCredentialOptions) {
+ this.mCallingPackage = callingPackage;
+ this.mGetCredentialOptions = getCredentialOptions;
+ }
+
+ private GetCredentialsRequest(@NonNull Parcel in) {
+ mCallingPackage = in.readString16NoHelper();
+ mGetCredentialOptions = in.createTypedArrayList(GetCredentialOption.CREATOR);
+ }
+
+ public static final @NonNull Creator<GetCredentialsRequest> CREATOR =
+ new Creator<GetCredentialsRequest>() {
+ @Override
+ public GetCredentialsRequest createFromParcel(Parcel in) {
+ return new GetCredentialsRequest(in);
+ }
+
+ @Override
+ public GetCredentialsRequest[] newArray(int size) {
+ return new GetCredentialsRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString16NoHelper(mCallingPackage);
+ dest.writeTypedList(mGetCredentialOptions);
+ }
+
+ /**
+ * Returns the calling package of the app requesting credentials.
+ */
+ public @NonNull String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /**
+ * Returns the list of type specific credential options to return credentials for.
+ */
+ public @NonNull List<GetCredentialOption> getGetCredentialOptions() {
+ return mGetCredentialOptions;
+ }
+
+ /**
+ * Builder for {@link GetCredentialsRequest}.
+ */
+ public static final class Builder {
+ private String mCallingPackage;
+ private List<GetCredentialOption> mGetCredentialOptions = new ArrayList<>();
+
+ /**
+ * Creates a new builder.
+ * @param callingPackage The calling package of the app requesting credentials.
+ *
+ * @throws IllegalArgumentException If {@code callingPackag}e is null or empty.
+ */
+ public Builder(@NonNull String callingPackage) {
+ mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage);
+ }
+
+ /**
+ * Sets the list of credential options.
+ *
+ * @throws NullPointerException If {@code getCredentialOptions} itself or any of its
+ * elements is null.
+ * @throws IllegalArgumentException If {@code getCredentialOptions} is empty.
+ */
+ public @NonNull Builder setGetCredentialOptions(
+ @NonNull List<GetCredentialOption> getCredentialOptions) {
+ Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+ "getCredentialOptions");
+ Preconditions.checkCollectionElementsNotNull(mGetCredentialOptions,
+ "getCredentialOptions");
+ mGetCredentialOptions = getCredentialOptions;
+ return this;
+ }
+
+ /**
+ * Adds a single {@link GetCredentialOption} object to the list of credential options.
+ *
+ * @throws NullPointerException If {@code getCredentialOption} is null.
+ */
+ public @NonNull Builder addGetCredentialOption(
+ @NonNull GetCredentialOption getCredentialOption) {
+ Objects.requireNonNull(getCredentialOption,
+ "getCredentialOption must not be null");
+ mGetCredentialOptions.add(getCredentialOption);
+ return this;
+ }
+
+ /**
+ * Builds a new {@link GetCredentialsRequest} instance.
+ *
+ * @throws NullPointerException If {@code getCredentialOptions} is null.
+ * @throws IllegalArgumentException If {@code getCredentialOptions} is empty, or if
+ * {@code callingPackage} is null or empty.
+ */
+ public @NonNull GetCredentialsRequest build() {
+ Preconditions.checkStringNotEmpty(mCallingPackage,
+ "Must set the calling package");
+ Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+ "getCredentialOptions");
+ return new GetCredentialsRequest(mCallingPackage, mGetCredentialOptions);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.aidl b/core/java/android/service/credentials/GetCredentialsResponse.aidl
new file mode 100644
index 0000000..0d8c635
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsResponse;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.java b/core/java/android/service/credentials/GetCredentialsResponse.java
new file mode 100644
index 0000000..293867b
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * Response from a credential provider, containing credential entries and other associated
+ * data to be shown on the account selector UI.
+ *
+ * @hide
+ */
+public final class GetCredentialsResponse implements Parcelable {
+ /** Content to be used for the UI. */
+ private final @Nullable CredentialsDisplayContent mCredentialsDisplayContent;
+
+ /**
+ * Authentication action that must be launched and completed before showing any content
+ * from the provider.
+ */
+ private final @Nullable Action mAuthenticationAction;
+
+ /**
+ * Creates a {@link GetCredentialsRequest} instance with an authentication action set.
+ * Providers must use this method when no content can be shown before authentication.
+ *
+ * @throws NullPointerException If {@code authenticationAction} is null.
+ */
+ public static @NonNull GetCredentialsResponse createWithAuthentication(
+ @NonNull Action authenticationAction) {
+ Objects.requireNonNull(authenticationAction,
+ "authenticationAction must not be null");
+ return new GetCredentialsResponse(null, authenticationAction);
+ }
+
+ /**
+ * Creates a {@link GetCredentialsRequest} instance with display content to be shown on the UI.
+ * Providers must use this method when there is content to be shown without top level
+ * authentication required.
+ *
+ * @throws NullPointerException If {@code credentialsDisplayContent} is null.
+ */
+ public static @NonNull GetCredentialsResponse createWithDisplayContent(
+ @NonNull CredentialsDisplayContent credentialsDisplayContent) {
+ Objects.requireNonNull(credentialsDisplayContent,
+ "credentialsDisplayContent must not be null");
+ return new GetCredentialsResponse(credentialsDisplayContent, null);
+ }
+
+ private GetCredentialsResponse(@Nullable CredentialsDisplayContent credentialsDisplayContent,
+ @Nullable Action authenticationAction) {
+ mCredentialsDisplayContent = credentialsDisplayContent;
+ mAuthenticationAction = authenticationAction;
+ }
+
+ private GetCredentialsResponse(@NonNull Parcel in) {
+ mCredentialsDisplayContent = in.readParcelable(CredentialsDisplayContent.class
+ .getClassLoader(), CredentialsDisplayContent.class);
+ mAuthenticationAction = in.readParcelable(Action.class.getClassLoader(), Action.class);
+ }
+
+ public static final @NonNull Creator<GetCredentialsResponse> CREATOR =
+ new Creator<GetCredentialsResponse>() {
+ @Override
+ public GetCredentialsResponse createFromParcel(Parcel in) {
+ return new GetCredentialsResponse(in);
+ }
+
+ @Override
+ public GetCredentialsResponse[] newArray(int size) {
+ return new GetCredentialsResponse[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mCredentialsDisplayContent, flags);
+ dest.writeParcelable(mAuthenticationAction, flags);
+ }
+
+ /**
+ * Returns whether the response contains a top level authentication action.
+ */
+ public @NonNull boolean isAuthenticationActionSet() {
+ return mAuthenticationAction != null;
+ }
+
+ /**
+ * Returns the authentication action to be invoked before any other content
+ * can be shown to the user.
+ */
+ public @NonNull Action getAuthenticationAction() {
+ return mAuthenticationAction;
+ }
+
+ /**
+ * Returns the credentialDisplayContent that does not require authentication, and
+ * can be shown to the user on the account selector UI.
+ */
+ public @NonNull CredentialsDisplayContent getCredentialsDisplayContent() {
+ return mCredentialsDisplayContent;
+ }
+}
diff --git a/core/java/android/service/credentials/ICreateCredentialCallback.aidl b/core/java/android/service/credentials/ICreateCredentialCallback.aidl
new file mode 100644
index 0000000..4cc76a4
--- /dev/null
+++ b/core/java/android/service/credentials/ICreateCredentialCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.CreateCredentialResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface ICreateCredentialCallback {
+ void onSuccess(in CreateCredentialResponse request);
+ void onFailure(int errorCode, in CharSequence message);
+}
\ No newline at end of file
diff --git a/core/java/android/service/credentials/ICredentialProviderService.aidl b/core/java/android/service/credentials/ICredentialProviderService.aidl
new file mode 100644
index 0000000..c68430c
--- /dev/null
+++ b/core/java/android/service/credentials/ICredentialProviderService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.os.ICancellationSignal;
+import android.service.credentials.GetCredentialsRequest;
+import android.service.credentials.CreateCredentialRequest;
+import android.service.credentials.IGetCredentialsCallback;
+import android.service.credentials.ICreateCredentialCallback;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface ICredentialProviderService {
+ void onGetCredentials(in GetCredentialsRequest request, in ICancellationSignal transport, in IGetCredentialsCallback callback);
+ void onCreateCredential(in CreateCredentialRequest request, in ICancellationSignal transport, in ICreateCredentialCallback callback);
+}
diff --git a/core/java/android/service/credentials/IGetCredentialsCallback.aidl b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
new file mode 100644
index 0000000..6e20c55
--- /dev/null
+++ b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.GetCredentialsResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface IGetCredentialsCallback {
+ void onSuccess(in GetCredentialsResponse response);
+ void onFailure(int errorCode, in CharSequence message);
+}
\ No newline at end of file
diff --git a/core/java/android/service/credentials/SaveEntry.java b/core/java/android/service/credentials/SaveEntry.java
new file mode 100644
index 0000000..28fec30
--- /dev/null
+++ b/core/java/android/service/credentials/SaveEntry.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * An entry to be shown on the UI. This entry represents where the credential to be created will
+ * be stored. Examples include user's account, family group etc.
+ *
+ * @hide
+ */
+public final class SaveEntry implements Parcelable {
+ private final @NonNull Slice mInfo;
+ private final @Nullable PendingIntent mPendingIntent;
+ private final @Nullable Credential mCredential;
+
+ private SaveEntry(@NonNull Parcel in) {
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ mCredential = in.readParcelable(Credential.class.getClassLoader(), Credential.class);
+ }
+
+ public static final @NonNull Creator<SaveEntry> CREATOR = new Creator<SaveEntry>() {
+ @Override
+ public SaveEntry createFromParcel(@NonNull Parcel in) {
+ return new SaveEntry(in);
+ }
+
+ @Override
+ public SaveEntry[] newArray(int size) {
+ return new SaveEntry[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ mCredential.writeToParcel(dest, flags);
+ }
+
+ /* package-private */ SaveEntry(
+ @NonNull Slice info,
+ @Nullable PendingIntent pendingIntent,
+ @Nullable Credential credential) {
+ this.mInfo = info;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInfo);
+ this.mPendingIntent = pendingIntent;
+ this.mCredential = credential;
+ }
+
+ /** Returns the info to be displayed with this save entry on the UI. */
+ public @NonNull Slice getInfo() {
+ return mInfo;
+ }
+
+ /** Returns the pendingIntent to be invoked when this save entry on the UI is selectcd. */
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ /** Returns the credential produced by the {@link CreateCredentialRequest}. */
+ public @Nullable Credential getCredential() {
+ return mCredential;
+ }
+
+ /**
+ * A builder for {@link SaveEntry}.
+ */
+ public static final class Builder {
+
+ private @NonNull Slice mInfo;
+ private @Nullable PendingIntent mPendingIntent;
+ private @Nullable Credential mCredential;
+
+ /**
+ * Builds the instance.
+ * @param info The info to be displayed with this save entry.
+ *
+ * @throws NullPointerException If {@code info} is null.
+ */
+ public Builder(@NonNull Slice info) {
+ mInfo = Objects.requireNonNull(info, "info must not be null");
+ }
+
+ /**
+ * Sets the pendingIntent to be invoked when this entry is selected by the user.
+ *
+ * @throws IllegalStateException If {@code credential} is already set. Must only set either
+ * {@code credential}, or the {@code pendingIntent}.
+ */
+ public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+ Preconditions.checkState(pendingIntent != null
+ && mCredential != null, "credential is already set. Must only set "
+ + "either the pendingIntent or the credential");
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the credential to be returned when this entry is selected by the user.
+ *
+ * @throws IllegalStateException If {@code pendingIntent} is already set. Must only
+ * set either the {@code pendingIntent}, or {@code credential}.
+ */
+ public @NonNull Builder setCredential(@Nullable Credential credential) {
+ Preconditions.checkState(credential != null && mPendingIntent != null,
+ "pendingIntent is already set. Must only set either the credential "
+ + "or the pendingIntent");
+ mCredential = credential;
+ return this;
+ }
+
+ /**
+ * Builds the instance.
+ *
+ * @throws IllegalStateException if both {@code pendingIntent} and {@code credential}
+ * are null.
+ */
+ public @NonNull SaveEntry build() {
+ Preconditions.checkState(mPendingIntent == null && mCredential == null,
+ "pendingIntent and credential both must not be null. Must set "
+ + "either the pendingIntnet or the credential");
+ return new SaveEntry(
+ mInfo,
+ mPendingIntent,
+ mCredential);
+ }
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1b46107..1285d1e 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -345,6 +345,12 @@
* Calling this a second time invalidates the previously created hotword detector
* which can no longer be used to manage recognition.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+ * AlwaysOnHotwordDetector.Callback)} or {@link #createHotwordDetector(PersistableBundle,
+ * SharedMemory, HotwordDetector.Callback)}, call this will throw an
+ * {@link IllegalArgumentException}.
+ *
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
* @param callback The callback to notify of detection events.
@@ -377,6 +383,10 @@
* <p>Note: The system will trigger hotword detection service after calling this function when
* all conditions meet the requirements.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+ * call this will throw an {@link IllegalArgumentException}.
+ *
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
* @param options Application configuration data provided by the
@@ -420,6 +430,14 @@
safelyShutdownAllHotwordDetectors();
}
+ for (HotwordDetector detector : mActiveHotwordDetectors) {
+ if (detector.isUsingHotwordDetectionService() != supportHotwordDetectionService) {
+ throw new IllegalArgumentException(
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
+ }
+ }
+
AlwaysOnHotwordDetector dspDetector = new AlwaysOnHotwordDetector(keyphrase, locale,
callback, mKeyphraseEnrollmentInfo, mSystemService,
getApplicationContext().getApplicationInfo().targetSdkVersion,
@@ -460,6 +478,10 @@
* devices where hardware filtering is available (such as through a DSP), it's highly
* recommended to use {@link #createAlwaysOnHotwordDetector} instead.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+ * call this will throw an {@link IllegalArgumentException}.
+ *
* @param options Application configuration data to be provided to the
* {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
* other contents that can be used to communicate with other processes.
@@ -490,7 +512,11 @@
safelyShutdownAllHotwordDetectors();
} else {
for (HotwordDetector detector : mActiveHotwordDetectors) {
- if (detector instanceof SoftwareHotwordDetector) {
+ if (!detector.isUsingHotwordDetectionService()) {
+ throw new IllegalArgumentException(
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
+ } else if (detector instanceof SoftwareHotwordDetector) {
throw new IllegalArgumentException(
"There is already an active SoftwareHotwordDetector. "
+ "It must be destroyed to create a new one.");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index b2a26fa..067946e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -67,6 +67,7 @@
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.ITaskFpsCallback;
+import android.window.ScreenCapture;
/**
* System private interface to the window manager.
@@ -968,4 +969,11 @@
* treatment.
*/
boolean isLetterboxBackgroundMultiColored();
+
+ /**
+ * Captures the entire display specified by the displayId using the args provided. If the args
+ * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured.
+ */
+ oneway void captureDisplay(int displayId, in @nullable ScreenCapture.CaptureArgs captureArgs,
+ in ScreenCapture.ScreenCaptureListener listener);
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c97eb73..d77e882 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -196,8 +196,6 @@
*/
public static boolean sRendererEnabled = true;
- public static boolean sTrimForeground = false;
-
/**
* Controls whether or not the renderer should aggressively trim
* memory. Note that this must not be set for any process that uses
@@ -205,9 +203,10 @@
* that do not go into the background.
*/
public static void enableForegroundTrimming() {
- sTrimForeground = true;
+ // TODO: Remove
}
+
/**
* Initialize HWUI for being in a system process like system_server
* Should not be called in non-system processes
@@ -218,9 +217,8 @@
// process.
if (!ActivityManager.isHighEndGfx()) {
sRendererEnabled = false;
- } else {
- enableForegroundTrimming();
}
+ setIsSystemOrPersistent();
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b6f775d..28fa77c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1766,9 +1766,6 @@
mAppVisibilityChanged = true;
scheduleTraversals();
}
- if (!mAppVisible) {
- WindowManagerGlobal.trimForeground();
- }
AnimationHandler.requestAnimatorsEnabled(mAppVisible, this);
}
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index d377565..4a9dc5b 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -19,9 +19,7 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -524,9 +522,6 @@
}
allViewsRemoved = mRoots.isEmpty();
}
- if (ThreadedRenderer.sTrimForeground) {
- doTrimForeground();
- }
// If we don't have any views anymore in our process, we no longer need the
// InsetsAnimationThread to save some resources.
@@ -543,65 +538,9 @@
return index;
}
- public static boolean shouldDestroyEglContext(int trimLevel) {
- // On low-end gfx devices we trim when memory is moderate;
- // on high-end devices we do this when low.
- if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
- return true;
- }
- if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
- && !ActivityManager.isHighEndGfx()) {
- return true;
- }
- return false;
- }
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void trimMemory(int level) {
-
- if (shouldDestroyEglContext(level)) {
- // Destroy all hardware surfaces and resources associated to
- // known windows
- synchronized (mLock) {
- for (int i = mRoots.size() - 1; i >= 0; --i) {
- mRoots.get(i).destroyHardwareResources();
- }
- }
- // Force a full memory flush
- level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
- }
-
ThreadedRenderer.trimMemory(level);
-
- if (ThreadedRenderer.sTrimForeground) {
- doTrimForeground();
- }
- }
-
- public static void trimForeground() {
- if (ThreadedRenderer.sTrimForeground) {
- WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
- wm.doTrimForeground();
- }
- }
-
- private void doTrimForeground() {
- boolean hasVisibleWindows = false;
- synchronized (mLock) {
- for (int i = mRoots.size() - 1; i >= 0; --i) {
- final ViewRootImpl root = mRoots.get(i);
- if (root.mView != null && root.getHostVisibility() == View.VISIBLE
- && root.mAttachInfo.mThreadedRenderer != null) {
- hasVisibleWindows = true;
- } else {
- root.destroyHardwareResources();
- }
- }
- }
- if (!hasVisibleWindows) {
- ThreadedRenderer.trimMemory(
- ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
- }
}
public void dumpGfxInfo(FileDescriptor fd, String[] args) {
diff --git a/core/java/android/window/ScreenCapture.aidl b/core/java/android/window/ScreenCapture.aidl
new file mode 100644
index 0000000..267a7c6
--- /dev/null
+++ b/core/java/android/window/ScreenCapture.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 android.window;
+
+/** @hide */
+parcelable ScreenCapture.CaptureArgs;
+
+/** @hide */
+parcelable ScreenCapture.ScreenshotHardwareBuffer;
+
+/** @hide */
+parcelable ScreenCapture.ScreenCaptureListener;
\ No newline at end of file
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index 887d027..8a7efb9 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -24,11 +24,17 @@
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.Log;
+import android.util.Pair;
import android.view.SurfaceControl;
+import libcore.util.NativeAllocationRegistry;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Handles display and layer captures for the system.
@@ -37,20 +43,26 @@
*/
public class ScreenCapture {
private static final String TAG = "ScreenCapture";
+ private static final int SCREENSHOT_WAIT_TIME_S = 1;
private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
- ScreenCaptureListener captureListener);
+ long captureListener);
private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
- ScreenCaptureListener captureListener);
+ long captureListener);
+ private static native long nativeCreateScreenCaptureListener(
+ Consumer<ScreenshotHardwareBuffer> consumer);
+ private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out);
+ private static native long nativeReadListenerFromParcel(Parcel in);
+ private static native long getNativeListenerFinalizer();
/**
- * @param captureArgs Arguments about how to take the screenshot
+ * @param captureArgs Arguments about how to take the screenshot
* @param captureListener A listener to receive the screenshot callback
* @hide
*/
public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
@NonNull ScreenCaptureListener captureListener) {
- return nativeCaptureDisplay(captureArgs, captureListener);
+ return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject);
}
/**
@@ -61,28 +73,29 @@
*/
public static ScreenshotHardwareBuffer captureDisplay(
DisplayCaptureArgs captureArgs) {
- SyncScreenCaptureListener
- screenCaptureListener = new SyncScreenCaptureListener();
-
- int status = captureDisplay(captureArgs, screenCaptureListener);
+ Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
+ int status = captureDisplay(captureArgs, syncScreenCapture.first);
if (status != 0) {
return null;
}
- return screenCaptureListener.waitForScreenshot();
+ try {
+ return syncScreenCapture.second.get();
+ } catch (Exception e) {
+ return null;
+ }
}
/**
* Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
*
- * @param layer The root layer to capture.
- * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
- * Rect()' or null if no cropping is desired. If the root layer does not
- * have a buffer or a crop set, then a non-empty source crop must be
- * specified.
- * @param frameScale The desired scale of the returned buffer; the raw
- * screen will be scaled up/down.
- *
+ * @param layer The root layer to capture.
+ * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+ * Rect()' or null if no cropping is desired. If the root layer does not
+ * have a buffer or a crop set, then a non-empty source crop must be
+ * specified.
+ * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
+ * up/down.
* @return Returns a HardwareBuffer that contains the layer capture.
* @hide
*/
@@ -94,15 +107,14 @@
/**
* Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
*
- * @param layer The root layer to capture.
- * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
- * Rect()' or null if no cropping is desired. If the root layer does not
- * have a buffer or a crop set, then a non-empty source crop must be
- * specified.
- * @param frameScale The desired scale of the returned buffer; the raw
- * screen will be scaled up/down.
- * @param format The desired pixel format of the returned buffer.
- *
+ * @param layer The root layer to capture.
+ * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+ * Rect()' or null if no cropping is desired. If the root layer does not
+ * have a buffer or a crop set, then a non-empty source crop must be
+ * specified.
+ * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
+ * up/down.
+ * @param format The desired pixel format of the returned buffer.
* @return Returns a HardwareBuffer that contains the layer capture.
* @hide
*/
@@ -120,21 +132,24 @@
/**
* @hide
*/
- public static ScreenshotHardwareBuffer captureLayers(
- LayerCaptureArgs captureArgs) {
- SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
-
- int status = captureLayers(captureArgs, screenCaptureListener);
+ public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
+ Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
+ int status = captureLayers(captureArgs, syncScreenCapture.first);
if (status != 0) {
return null;
}
- return screenCaptureListener.waitForScreenshot();
+ try {
+ return syncScreenCapture.second.get();
+ } catch (Exception e) {
+ return null;
+ }
}
/**
* Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer
* handles to exclude.
+ *
* @hide
*/
public static ScreenshotHardwareBuffer captureLayersExcluding(SurfaceControl layer,
@@ -150,24 +165,13 @@
}
/**
- * @param captureArgs Arguments about how to take the screenshot
+ * @param captureArgs Arguments about how to take the screenshot
* @param captureListener A listener to receive the screenshot callback
* @hide
*/
public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
@NonNull ScreenCaptureListener captureListener) {
- return nativeCaptureLayers(captureArgs, captureListener);
- }
-
- /**
- * @hide
- */
- public interface ScreenCaptureListener {
- /**
- * The callback invoked when the screen capture is complete.
- * @param hardwareBuffer Data containing info about the screen capture.
- */
- void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
+ return nativeCaptureLayers(captureArgs, captureListener.mNativeObject);
}
/**
@@ -190,15 +194,16 @@
mContainsHdrLayers = containsHdrLayers;
}
- /**
- * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
- * @param hardwareBuffer The existing HardwareBuffer object
- * @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
- * @param containsSecureLayers Indicates whether this graphic buffer contains captured
- * contents of secure layers, in which case the screenshot
- * should not be persisted.
- * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
- */
+ /**
+ * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
+ *
+ * @param hardwareBuffer The existing HardwareBuffer object
+ * @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
+ * @param containsSecureLayers Indicates whether this graphic buffer contains captured
+ * contents of secure layers, in which case the screenshot
+ * should not be persisted.
+ * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
+ */
private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) {
ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]);
@@ -220,6 +225,7 @@
public boolean containsSecureLayers() {
return mContainsSecureLayers;
}
+
/**
* Returns whether the screenshot contains at least one HDR layer.
* This information may be useful for informing the display whether this screenshot
@@ -234,7 +240,7 @@
* Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
* into
* a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
- *
+ * <p>
* CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
* directly
* use the {@link HardwareBuffer} directly.
@@ -250,44 +256,23 @@
}
}
- private static class SyncScreenCaptureListener implements ScreenCaptureListener {
- private static final int SCREENSHOT_WAIT_TIME_S = 1;
- private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
- private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
-
- @Override
- public void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
- mScreenshotHardwareBuffer = hardwareBuffer;
- mCountDownLatch.countDown();
- }
-
- private ScreenshotHardwareBuffer waitForScreenshot() {
- try {
- mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
- } catch (Exception e) {
- Log.e(TAG, "Failed to wait for screen capture result", e);
- }
-
- return mScreenshotHardwareBuffer;
- }
- }
-
/**
* A common arguments class used for various screenshot requests. This contains arguments that
* are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
+ *
* @hide
*/
- private abstract static class CaptureArgs {
- private final int mPixelFormat;
- private final Rect mSourceCrop = new Rect();
- private final float mFrameScaleX;
- private final float mFrameScaleY;
- private final boolean mCaptureSecureLayers;
- private final boolean mAllowProtected;
- private final long mUid;
- private final boolean mGrayscale;
+ public static class CaptureArgs implements Parcelable {
+ public final int mPixelFormat;
+ public final Rect mSourceCrop = new Rect();
+ public final float mFrameScaleX;
+ public final float mFrameScaleY;
+ public final boolean mCaptureSecureLayers;
+ public final boolean mAllowProtected;
+ public final long mUid;
+ public final boolean mGrayscale;
- private CaptureArgs(Builder<? extends Builder<?>> builder) {
+ private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) {
mPixelFormat = builder.mPixelFormat;
mSourceCrop.set(builder.mSourceCrop);
mFrameScaleX = builder.mFrameScaleX;
@@ -298,12 +283,23 @@
mGrayscale = builder.mGrayscale;
}
+ private CaptureArgs(Parcel in) {
+ mPixelFormat = in.readInt();
+ mSourceCrop.readFromParcel(in);
+ mFrameScaleX = in.readFloat();
+ mFrameScaleY = in.readFloat();
+ mCaptureSecureLayers = in.readBoolean();
+ mAllowProtected = in.readBoolean();
+ mUid = in.readLong();
+ mGrayscale = in.readBoolean();
+ }
+
/**
* The Builder class used to construct {@link CaptureArgs}
*
- * @param <T> A builder that extends {@link Builder}
+ * @param <T> A builder that extends {@link CaptureArgs.Builder}
*/
- abstract static class Builder<T extends Builder<T>> {
+ public static class Builder<T extends CaptureArgs.Builder<T>> {
private int mPixelFormat = PixelFormat.RGBA_8888;
private final Rect mSourceCrop = new Rect();
private float mFrameScaleX = 1;
@@ -314,6 +310,14 @@
private boolean mGrayscale;
/**
+ * Construct a new {@link CaptureArgs} with the set parameters. The builder remains
+ * valid.
+ */
+ public CaptureArgs build() {
+ return new CaptureArgs(this);
+ }
+
+ /**
* The desired pixel format of the returned buffer.
*/
public T setPixelFormat(int pixelFormat) {
@@ -395,15 +399,47 @@
/**
* Each sub class should return itself to allow the builder to chain properly
*/
- abstract T getThis();
+ T getThis() {
+ return (T) this;
+ }
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPixelFormat);
+ mSourceCrop.writeToParcel(dest, flags);
+ dest.writeFloat(mFrameScaleX);
+ dest.writeFloat(mFrameScaleY);
+ dest.writeBoolean(mCaptureSecureLayers);
+ dest.writeBoolean(mAllowProtected);
+ dest.writeLong(mUid);
+ dest.writeBoolean(mGrayscale);
+ }
+
+ public static final Parcelable.Creator<CaptureArgs> CREATOR =
+ new Parcelable.Creator<CaptureArgs>() {
+ @Override
+ public CaptureArgs createFromParcel(Parcel in) {
+ return new CaptureArgs(in);
+ }
+
+ @Override
+ public CaptureArgs[] newArray(int size) {
+ return new CaptureArgs[size];
+ }
+ };
}
/**
* The arguments class used to make display capture requests.
*
- * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
* @hide
+ * @see #nativeCaptureDisplay(DisplayCaptureArgs, long)
*/
public static class DisplayCaptureArgs extends CaptureArgs {
private final IBinder mDisplayToken;
@@ -488,8 +524,8 @@
/**
* The arguments class used to make layer capture requests.
*
- * @see #nativeCaptureLayers(LayerCaptureArgs, ScreenCaptureListener)
* @hide
+ * @see #nativeCaptureLayers(LayerCaptureArgs, long)
*/
public static class LayerCaptureArgs extends CaptureArgs {
private final long mNativeLayer;
@@ -530,6 +566,17 @@
return new LayerCaptureArgs(this);
}
+ public Builder(SurfaceControl layer, CaptureArgs args) {
+ setLayer(layer);
+ setPixelFormat(args.mPixelFormat);
+ setSourceCrop(args.mSourceCrop);
+ setFrameScale(args.mFrameScaleX, args.mFrameScaleY);
+ setCaptureSecureLayers(args.mCaptureSecureLayers);
+ setAllowProtected(args.mAllowProtected);
+ setUid(args.mUid);
+ setGrayscale(args.mGrayscale);
+ }
+
public Builder(SurfaceControl layer) {
setLayer(layer);
}
@@ -542,7 +589,6 @@
return this;
}
-
/**
* An array of layer handles to exclude.
*/
@@ -564,8 +610,109 @@
Builder getThis() {
return this;
}
-
}
}
+ /**
+ * The object used to receive the results when invoking screen capture requests via
+ * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or
+ * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
+ *
+ * This listener can only be used for a single call to capture content call.
+ */
+ public static class ScreenCaptureListener implements Parcelable {
+ private final long mNativeObject;
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer());
+
+ /**
+ * @param consumer The callback invoked when the screen capture is complete.
+ */
+ public ScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
+ mNativeObject = nativeCreateScreenCaptureListener(consumer);
+ sRegistry.registerNativeAllocation(this, mNativeObject);
+ }
+
+ private ScreenCaptureListener(Parcel in) {
+ if (in.readBoolean()) {
+ mNativeObject = nativeReadListenerFromParcel(in);
+ sRegistry.registerNativeAllocation(this, mNativeObject);
+ } else {
+ mNativeObject = 0;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (mNativeObject == 0) {
+ dest.writeBoolean(false);
+ } else {
+ dest.writeBoolean(true);
+ nativeWriteListenerToParcel(mNativeObject, dest);
+ }
+ }
+
+ public static final Parcelable.Creator<ScreenCaptureListener> CREATOR =
+ new Parcelable.Creator<ScreenCaptureListener>() {
+ @Override
+ public ScreenCaptureListener createFromParcel(Parcel in) {
+ return new ScreenCaptureListener(in);
+ }
+
+ @Override
+ public ScreenCaptureListener[] newArray(int size) {
+ return new ScreenCaptureListener[0];
+ }
+ };
+ }
+
+ /**
+ * A helper method to handle the async screencapture callbacks synchronously. This should only
+ * be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
+ *
+ * @return a Pair that holds the {@link ScreenCaptureListener} that should be used for capture
+ * calls into SurfaceFlinger and a {@link ScreenshotSync} object to retrieve the results.
+ */
+ public static Pair<ScreenCaptureListener, ScreenshotSync> createSyncCaptureListener() {
+ final ScreenshotSync screenshotSync = new ScreenshotSync();
+ final ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener(
+ screenshotSync::setScreenshotHardwareBuffer);
+ return new Pair<>(screenCaptureListener, screenshotSync);
+ }
+
+ /**
+ * Helper class to synchronously get the {@link ScreenshotHardwareBuffer} when calling
+ * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
+ * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
+ */
+ public static class ScreenshotSync {
+ private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+ private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
+
+ private void setScreenshotHardwareBuffer(
+ ScreenshotHardwareBuffer screenshotHardwareBuffer) {
+ mScreenshotHardwareBuffer = screenshotHardwareBuffer;
+ mCountDownLatch.countDown();
+ }
+
+ /**
+ * Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the
+ * screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds.
+ */
+ public ScreenshotHardwareBuffer get() {
+ try {
+ mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+ return mScreenshotHardwareBuffer;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to wait for screen capture result", e);
+ return null;
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/jank/EventLogTags.logtags b/core/java/com/android/internal/jank/EventLogTags.logtags
new file mode 100644
index 0000000..6139bce
--- /dev/null
+++ b/core/java/com/android/internal/jank/EventLogTags.logtags
@@ -0,0 +1,10 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.jank;
+
+# Marks a request to start tracing a CUJ. Doesn't mean the request was executed.
+37001 jank_cuj_events_begin_request (CUJ Type|1|5)
+# Marks a request to end tracing a CUJ. Doesn't mean the request was executed.
+37002 jank_cuj_events_end_request (CUJ Type|1|5)
+# Marks a request to cancel tracing a CUJ. Doesn't mean the request was executed.
+37003 jank_cuj_events_cancel_request (CUJ Type|1|5)
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 40d192e..76f33a6 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -541,6 +541,7 @@
public boolean begin(@NonNull Configuration.Builder builder) {
try {
final Configuration config = builder.build();
+ EventLogTags.writeJankCujEventsBeginRequest(config.mCujType);
final TrackerResult result = new TrackerResult();
final boolean success = config.getHandler().runWithScissors(
() -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT);
@@ -614,6 +615,7 @@
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
public boolean end(@CujType int cujType) {
+ EventLogTags.writeJankCujEventsEndRequest(cujType);
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
@@ -651,6 +653,7 @@
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
public boolean cancel(@CujType int cujType) {
+ EventLogTags.writeJankCujEventsCancelRequest(cujType);
return cancel(cujType, REASON_CANCEL_NORMAL);
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b5a78b0..b60ec9f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -631,7 +631,7 @@
jshort density, jobject typed_value,
jboolean resolve_references) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- ResourceTimer _tag(ResourceTimer::Counter::GetResourceValue);
+ ResourceTimer _timer(ResourceTimer::Counter::GetResourceValue);
auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
static_cast<uint16_t>(density));
if (!value.has_value()) {
@@ -1234,7 +1234,7 @@
}
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- ResourceTimer _tag(ResourceTimer::Counter::RetrieveAttributes);
+ ResourceTimer _timer(ResourceTimer::Counter::RetrieveAttributes);
ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
auto result =
RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs),
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 3bada15..c1929c6 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -61,9 +61,8 @@
} gLayerCaptureArgsClassInfo;
static struct {
- jclass clazz;
- jmethodID onScreenCaptureComplete;
-} gScreenCaptureListenerClassInfo;
+ jmethodID accept;
+} gConsumerClassInfo;
static struct {
jclass clazz;
@@ -98,14 +97,14 @@
public:
explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
- mScreenCaptureListenerObject = env->NewGlobalRef(jobject);
- LOG_ALWAYS_FATAL_IF(!mScreenCaptureListenerObject, "Failed to make global ref");
+ mConsumerObject = env->NewGlobalRef(jobject);
+ LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref");
}
~ScreenCaptureListenerWrapper() {
- if (mScreenCaptureListenerObject) {
- getenv()->DeleteGlobalRef(mScreenCaptureListenerObject);
- mScreenCaptureListenerObject = nullptr;
+ if (mConsumerObject) {
+ getenv()->DeleteGlobalRef(mConsumerObject);
+ mConsumerObject = nullptr;
}
}
@@ -113,9 +112,8 @@
const gui::ScreenCaptureResults& captureResults) override {
JNIEnv* env = getenv();
if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
- env->CallVoidMethod(mScreenCaptureListenerObject,
- gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
- checkAndClearException(env, "onScreenCaptureComplete");
+ env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr);
+ checkAndClearException(env, "accept");
return binder::Status::ok();
}
captureResults.fenceResult.value()->waitForever(LOG_TAG);
@@ -130,17 +128,15 @@
captureResults.capturedSecureLayers,
captureResults.capturedHdrLayers);
checkAndClearException(env, "builder");
- env->CallVoidMethod(mScreenCaptureListenerObject,
- gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
- screenshotHardwareBuffer);
- checkAndClearException(env, "onScreenCaptureComplete");
+ env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer);
+ checkAndClearException(env, "accept");
env->DeleteLocalRef(jhardwareBuffer);
env->DeleteLocalRef(screenshotHardwareBuffer);
return binder::Status::ok();
}
private:
- jobject mScreenCaptureListenerObject;
+ jobject mConsumerObject;
JavaVM* mVm;
JNIEnv* getenv() {
@@ -194,7 +190,7 @@
}
static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
- jobject screenCaptureListenerObject) {
+ jlong screenCaptureListenerObject) {
const DisplayCaptureArgs captureArgs =
displayCaptureArgsFromObject(env, displayCaptureArgsObject);
@@ -202,13 +198,13 @@
return BAD_VALUE;
}
- sp<IScreenCaptureListener> captureListener =
- sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject);
+ sp<gui::IScreenCaptureListener> captureListener =
+ reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
return ScreenshotClient::captureDisplay(captureArgs, captureListener);
}
static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
- jobject screenCaptureListenerObject) {
+ jlong screenCaptureListenerObject) {
LayerCaptureArgs captureArgs;
getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
@@ -238,21 +234,70 @@
}
}
- sp<IScreenCaptureListener> captureListener =
- sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject);
+ sp<gui::IScreenCaptureListener> captureListener =
+ reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
return ScreenshotClient::captureLayers(captureArgs, captureListener);
}
+static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) {
+ sp<gui::IScreenCaptureListener> listener =
+ sp<ScreenCaptureListenerWrapper>::make(env, consumerObj);
+ listener->incStrong((void*)nativeCreateScreenCaptureListener);
+ return reinterpret_cast<jlong>(listener.get());
+}
+
+static void nativeWriteListenerToParcel(JNIEnv* env, jclass clazz, jlong nativeObject,
+ jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
+ if (parcel == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+ ScreenCaptureListenerWrapper* const self =
+ reinterpret_cast<ScreenCaptureListenerWrapper*>(nativeObject);
+ if (self != nullptr) {
+ parcel->writeStrongBinder(IInterface::asBinder(self));
+ }
+}
+
+static jlong nativeReadListenerFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
+ if (parcel == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return 0;
+ }
+ sp<gui::IScreenCaptureListener> listener =
+ interface_cast<gui::IScreenCaptureListener>(parcel->readStrongBinder());
+ if (listener == nullptr) {
+ return 0;
+ }
+ listener->incStrong((void*)nativeCreateScreenCaptureListener);
+ return reinterpret_cast<jlong>(listener.get());
+}
+
+void destroyNativeListener(void* ptr) {
+ ScreenCaptureListenerWrapper* listener = reinterpret_cast<ScreenCaptureListenerWrapper*>(ptr);
+ listener->decStrong((void*)nativeCreateScreenCaptureListener);
+}
+
+static jlong getNativeListenerFinalizer(JNIEnv* env, jclass clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeListener));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sScreenCaptureMethods[] = {
// clang-format off
- {"nativeCaptureDisplay",
- "(Landroid/window/ScreenCapture$DisplayCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I",
+ {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
(void*)nativeCaptureDisplay },
- {"nativeCaptureLayers",
- "(Landroid/window/ScreenCapture$LayerCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I",
+ {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I",
(void*)nativeCaptureLayers },
+ {"nativeCreateScreenCaptureListener", "(Ljava/util/function/Consumer;)J",
+ (void*)nativeCreateScreenCaptureListener },
+ {"nativeWriteListenerToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteListenerToParcel },
+ {"nativeReadListenerFromParcel", "(Landroid/os/Parcel;)J",
+ (void*)nativeReadListenerFromParcel },
+ {"getNativeListenerFinalizer", "()J", (void*)getNativeListenerFinalizer },
// clang-format on
};
@@ -293,12 +338,8 @@
gLayerCaptureArgsClassInfo.childrenOnly =
GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
- jclass screenCaptureListenerClazz =
- FindClassOrDie(env, "android/window/ScreenCapture$ScreenCaptureListener");
- gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
- gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
- GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
- "(Landroid/window/ScreenCapture$ScreenshotHardwareBuffer;)V");
+ jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
+ gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
jclass screenshotGraphicsBufferClazz =
FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer");
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 3ecc7ff..bf65af3 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -131,7 +131,7 @@
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
Light.LIGHT_CAPABILITY_BRIGHTNESS),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
0 /* capabilities */)
};
@@ -150,13 +150,13 @@
Light[] mockedLights = {
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(4 /* id */, "Light4", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB)
+ Light.LIGHT_CAPABILITY_COLOR_RGB)
};
mockLights(mockedLights);
@@ -204,7 +204,7 @@
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
0 /* capabilities */),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
Light.LIGHT_CAPABILITY_BRIGHTNESS)
};
@@ -239,9 +239,9 @@
@Test
public void testLightCapabilities() throws Exception {
Light light = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
assertThat(light.getType()).isEqualTo(Light.LIGHT_TYPE_INPUT);
- assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_RGB
+ assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_COLOR_RGB
| Light.LIGHT_CAPABILITY_BRIGHTNESS);
assertTrue(light.hasBrightnessControl());
assertTrue(light.hasRgbControl());
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 0e67f1f..c6731d1 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -47,9 +47,7 @@
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Optional;
import java.util.concurrent.Executor;
-import java.util.stream.Stream;
import sun.misc.Cleaner;
@@ -1142,6 +1140,16 @@
}
/**
+ * Sets whether or not the current process is a system or persistent process. Used to influence
+ * the chosen memory usage policy.
+ *
+ * @hide
+ **/
+ public static void setIsSystemOrPersistent() {
+ nSetIsSystemOrPersistent(true);
+ }
+
+ /**
* Returns true if HardwareRender will produce output.
*
* This value is global to the process and affects all uses of HardwareRenderer,
@@ -1204,30 +1212,6 @@
private static class ProcessInitializer {
static ProcessInitializer sInstance = new ProcessInitializer();
- // Magic values from android/data_space.h
- private static final int INTERNAL_DATASPACE_SRGB = 142671872;
- private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
- private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
-
- private enum Dataspace {
- DISPLAY_P3(ColorSpace.Named.DISPLAY_P3, INTERNAL_DATASPACE_DISPLAY_P3),
- SCRGB(ColorSpace.Named.EXTENDED_SRGB, INTERNAL_DATASPACE_SCRGB),
- SRGB(ColorSpace.Named.SRGB, INTERNAL_DATASPACE_SRGB);
-
- private final ColorSpace.Named mColorSpace;
- private final int mNativeDataspace;
- Dataspace(ColorSpace.Named colorSpace, int nativeDataspace) {
- this.mColorSpace = colorSpace;
- this.mNativeDataspace = nativeDataspace;
- }
-
- static Optional<Dataspace> find(ColorSpace colorSpace) {
- return Stream.of(Dataspace.values())
- .filter(d -> ColorSpace.get(d.mColorSpace).equals(colorSpace))
- .findFirst();
- }
- }
-
private boolean mInitialized = false;
private boolean mDisplayInitialized = false;
@@ -1296,6 +1280,7 @@
initDisplayInfo();
nSetIsHighEndGfx(ActivityManager.isHighEndGfx());
+ nSetIsLowRam(ActivityManager.isLowRamDeviceStatic());
// Defensively clear out the context in case we were passed a context that can leak
// if we live longer than it, e.g. an activity context.
mContext = null;
@@ -1314,26 +1299,55 @@
return;
}
- Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
- if (display == null) {
+ final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ if (defaultDisplay == null) {
Log.d(LOG_TAG, "Failed to find default display for display-based configuration");
return;
}
- Dataspace wideColorDataspace =
- Optional.ofNullable(display.getPreferredWideGamutColorSpace())
- .flatMap(Dataspace::find)
- // Default to SRGB if the display doesn't support wide color
- .orElse(Dataspace.SRGB);
+ final Display[] allDisplays = dm.getDisplays();
+ if (allDisplays.length == 0) {
+ Log.d(LOG_TAG, "Failed to query displays");
+ return;
+ }
- // Grab the physical screen dimensions from the active display mode
- // Strictly speaking the screen resolution may not always be constant - it is for
- // sizing the font cache for the underlying rendering thread. Since it's a
- // heuristic we don't need to be always 100% correct.
- Mode activeMode = display.getMode();
- nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
- display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
- display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
+ final Mode activeMode = defaultDisplay.getMode();
+ final ColorSpace defaultWideColorSpace =
+ defaultDisplay.getPreferredWideGamutColorSpace();
+ int wideColorDataspace = defaultWideColorSpace != null
+ ? defaultWideColorSpace.getDataSpace() : 0;
+ // largest width & height are used to size the default HWUI cache sizes. So find the
+ // largest display resolution we could encounter & use that as the guidance. The actual
+ // memory policy in play will interpret these values differently.
+ int largestWidth = activeMode.getPhysicalWidth();
+ int largestHeight = activeMode.getPhysicalHeight();
+
+ for (int i = 0; i < allDisplays.length; i++) {
+ final Display display = allDisplays[i];
+ // Take the first wide gamut dataspace as the source of truth
+ // Possibly should do per-HardwareRenderer wide gamut dataspace so we can use the
+ // target display's ideal instead
+ if (wideColorDataspace == 0) {
+ ColorSpace cs = display.getPreferredWideGamutColorSpace();
+ if (cs != null) {
+ wideColorDataspace = cs.getDataSpace();
+ }
+ }
+ Mode[] modes = display.getSupportedModes();
+ for (int j = 0; j < modes.length; j++) {
+ Mode mode = modes[j];
+ int width = mode.getPhysicalWidth();
+ int height = mode.getPhysicalHeight();
+ if ((width * height) > (largestWidth * largestHeight)) {
+ largestWidth = width;
+ largestHeight = height;
+ }
+ }
+ }
+
+ nInitDisplayInfo(largestWidth, largestHeight, defaultDisplay.getRefreshRate(),
+ wideColorDataspace, defaultDisplay.getAppVsyncOffsetNanos(),
+ defaultDisplay.getPresentationDeadlineNanos());
mDisplayInitialized = true;
}
@@ -1418,6 +1432,10 @@
private static native void nSetIsHighEndGfx(boolean isHighEndGfx);
+ private static native void nSetIsLowRam(boolean isLowRam);
+
+ private static native void nSetIsSystemOrPersistent(boolean isSystemOrPersistent);
+
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
private static native void nDestroy(long nativeProxy, long rootRenderNode);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f4dda4c..80cdd1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -484,12 +484,14 @@
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellCommandHandler,
- taskStackListener, desktopModeTaskRepository, mainExecutor));
+ taskStackListener, activityTaskManager, desktopModeTaskRepository,
+ mainExecutor));
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e784261..37a50b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -188,14 +188,16 @@
@ShellMainThread Choreographer mainChoreographer,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
- SyncTransactionQueue syncQueue) {
+ SyncTransactionQueue syncQueue,
+ @DynamicOverride DesktopModeController desktopModeController) {
return new CaptionWindowDecorViewModel(
context,
mainHandler,
mainChoreographer,
taskOrganizer,
displayController,
- syncQueue);
+ syncQueue,
+ desktopModeController);
}
//
@@ -318,6 +320,7 @@
ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
+ PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -337,11 +340,12 @@
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(
context, shellInit, shellCommandHandler, shellController,
- displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
- pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
- pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
- windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- displayInsetsController, oneHandedController, mainExecutor));
+ displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
+ pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
+ phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler,
+ pipTransitionController, windowManagerShellWrapper, taskStackListener,
+ pipParamsChangedForwarder, displayInsetsController, oneHandedController,
+ mainExecutor));
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 9474cfe..99739c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -177,6 +177,25 @@
}
/**
+ * Turn desktop mode on or off
+ * @param active the desired state for desktop mode setting
+ */
+ public void setDesktopModeActive(boolean active) {
+ int value = active ? 1 : 0;
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, value);
+ }
+
+ /**
+ * Returns the windowing mode of the display area with the specified displayId.
+ * @param displayId
+ * @return
+ */
+ public int getDisplayAreaWindowingMode(int displayId) {
+ return mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
+ .configuration.windowConfiguration.getWindowingMode();
+ }
+
+ /**
* A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
*/
private final class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index b32c3ee..6728c00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -195,6 +195,17 @@
}
/**
+ * Returns true if the PiP window is currently being animated.
+ */
+ public boolean isAnimating() {
+ PipAnimationController.PipTransitionAnimator animator = getCurrentAnimator();
+ if (animator != null && animator.isRunning()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Quietly cancel the animator by removing the listeners first.
*/
static void quietCancel(@NonNull ValueAnimator animator) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 1a52d8c..f170e77 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -324,19 +324,6 @@
return mPipTransitionController;
}
- /**
- * Returns true if the PiP window is currently being animated.
- */
- public boolean isAnimating() {
- // TODO(b/183746978) move this to PipAnimationController, and inject that in PipController
- PipAnimationController.PipTransitionAnimator animator =
- mPipAnimationController.getCurrentAnimator();
- if (animator != null && animator.isRunning()) {
- return true;
- }
- return false;
- }
-
public Rect getCurrentOrAnimatingBounds() {
PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getCurrentAnimator();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index bc8191d..af47666 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -130,6 +130,7 @@
private DisplayController mDisplayController;
private PipInputConsumer mPipInputConsumer;
private WindowManagerShellWrapper mWindowManagerShellWrapper;
+ private PipAnimationController mPipAnimationController;
private PipAppOpsListener mAppOpsListener;
private PipMediaController mMediaController;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -158,7 +159,7 @@
return;
}
// if there is another animation ongoing, wait for it to finish and try again
- if (mPipTaskOrganizer.isAnimating()) {
+ if (mPipAnimationController.isAnimating()) {
mMainExecutor.removeCallbacks(
mMovePipInResponseToKeepClearAreasChangeCallback);
mMainExecutor.executeDelayed(
@@ -368,6 +369,7 @@
ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
+ PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -392,11 +394,12 @@
}
return new PipController(context, shellInit, shellCommandHandler, shellController,
- displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
- pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
- pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
- windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- displayInsetsController, oneHandedController, mainExecutor)
+ displayController, pipAnimationController, pipAppOpsListener,
+ pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
+ pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
+ pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+ taskStackListener, pipParamsChangedForwarder, displayInsetsController,
+ oneHandedController, mainExecutor)
.mImpl;
}
@@ -405,6 +408,7 @@
ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
+ PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -445,6 +449,7 @@
mMediaController = pipMediaController;
mMenuController = phonePipMenuController;
mTouchHandler = pipTouchHandler;
+ mPipAnimationController = pipAnimationController;
mAppOpsListener = pipAppOpsListener;
mOneHandedController = oneHandedController;
mPipTransitionController = pipTransitionController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index a5748f6..2a62552 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -17,6 +17,11 @@
package com.android.wm.shell.recents;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Interface for interacting with the recent tasks.
@@ -29,4 +34,11 @@
default IRecentTasks createExternalInterface() {
return null;
}
+
+ /**
+ * Gets the set of recent tasks.
+ */
+ default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
+ Consumer<List<GroupedRecentTaskInfo>> callback) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 6409e70..02b5a35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -58,6 +58,8 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Manages the recent task list from the system, caching it as necessary.
@@ -72,6 +74,7 @@
private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasks mImpl = new RecentTasksImpl();
+ private final ActivityTaskManager mActivityTaskManager;
private IRecentTasksListener mListener;
private final boolean mIsDesktopMode;
@@ -96,6 +99,7 @@
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
@@ -103,17 +107,19 @@
return null;
}
return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
- desktopModeTaskRepository, mainExecutor);
+ activityTaskManager, desktopModeTaskRepository, mainExecutor);
}
RecentTasksController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
ShellExecutor mainExecutor) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
+ mActivityTaskManager = activityTaskManager;
mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
mDesktopModeTaskRepository = desktopModeTaskRepository;
@@ -270,15 +276,10 @@
}
@VisibleForTesting
- List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) {
- return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId);
- }
-
- @VisibleForTesting
ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
// Note: the returned task list is from the most-recent to least-recent order
- final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags,
- userId);
+ final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
+ maxNum, flags, userId);
// Make a mapping of task id -> task info
final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
@@ -335,8 +336,9 @@
if (componentName == null) {
return null;
}
- List<ActivityManager.RecentTaskInfo> tasks = getRawRecentTasks(Integer.MAX_VALUE,
- ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
+ List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+ ActivityManager.getCurrentUser());
for (int i = 0; i < tasks.size(); i++) {
final ActivityManager.RecentTaskInfo task = tasks.get(i);
if (task.isVisible) {
@@ -374,6 +376,16 @@
mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this);
return mIRecentTasks;
}
+
+ @Override
+ public void getRecentTasks(int maxNum, int flags, int userId, Executor executor,
+ Consumer<List<GroupedRecentTaskInfo>> callback) {
+ mMainExecutor.execute(() -> {
+ List<GroupedRecentTaskInfo> tasks =
+ RecentTasksController.this.getRecentTasks(maxNum, flags, userId);
+ executor.execute(() -> callback.accept(tasks));
+ });
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 9c7131a..9e49b51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -36,6 +37,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
@@ -53,6 +55,7 @@
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
private FreeformTaskTransitionStarter mTransitionStarter;
+ private DesktopModeController mDesktopModeController;
public CaptionWindowDecorViewModel(
Context context,
@@ -60,7 +63,8 @@
Choreographer mainChoreographer,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
- SyncTransactionQueue syncQueue) {
+ SyncTransactionQueue syncQueue,
+ DesktopModeController desktopModeController) {
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -68,6 +72,7 @@
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
mSyncQueue = syncQueue;
+ mDesktopModeController = desktopModeController;
}
@Override
@@ -210,8 +215,10 @@
}
private void handleEventForMove(MotionEvent e) {
- if (mTaskOrganizer.getRunningTaskInfo(mTaskId).getWindowingMode()
- == WINDOWING_MODE_FULLSCREEN) {
+ RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ int windowingMode = mDesktopModeController
+ .getDisplayAreaWindowingMode(taskInfo.displayId);
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
return;
}
switch (e.getActionMasked()) {
@@ -229,8 +236,14 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ int statusBarHeight = mDisplayController.getDisplayLayout(taskInfo.displayId)
+ .stableInsets().top;
mDragResizeCallback.onDragResizeEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+ if (e.getRawY(dragPointerIdx) <= statusBarHeight
+ && windowingMode == WINDOWING_MODE_FREEFORM) {
+ mDesktopModeController.setDesktopModeActive(false);
+ }
break;
}
}
@@ -240,6 +253,7 @@
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.IS_SUPPORTED
+ && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
&& mDisplayController.getDisplayContext(taskInfo.displayId)
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index a8d3bdc..1e08f1e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -48,6 +48,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -85,6 +86,7 @@
@Mock private ShellCommandHandler mMockShellCommandHandler;
@Mock private DisplayController mMockDisplayController;
@Mock private PhonePipMenuController mMockPhonePipMenuController;
+ @Mock private PipAnimationController mMockPipAnimationController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
@Mock private PhonePipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
@@ -117,8 +119,8 @@
mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler,
mMockExecutor));
mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
- mShellController, mMockDisplayController, mMockPipAppOpsListener,
- mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+ mShellController, mMockDisplayController, mMockPipAnimationController,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
@@ -183,8 +185,8 @@
ShellInit shellInit = new ShellInit(mMockExecutor);
assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
- mShellController, mMockDisplayController, mMockPipAppOpsListener,
- mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+ mShellController, mMockDisplayController, mMockPipAnimationController,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 70fee2b..b8aaaa7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -40,6 +40,7 @@
import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
@@ -52,7 +53,6 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
@@ -68,7 +68,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
/**
* Tests for {@link RecentTasksController}.
@@ -85,11 +87,13 @@
private ShellCommandHandler mShellCommandHandler;
@Mock
private DesktopModeTaskRepository mDesktopModeTaskRepository;
+ @Mock
+ private ActivityTaskManager mActivityTaskManager;
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
private ShellInit mShellInit;
- private ShellExecutor mMainExecutor;
+ private TestShellExecutor mMainExecutor;
@Before
public void setUp() {
@@ -97,8 +101,8 @@
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
- mShellCommandHandler, mTaskStackListener, Optional.of(mDesktopModeTaskRepository),
- mMainExecutor));
+ mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
+ Optional.of(mDesktopModeTaskRepository), mMainExecutor));
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
@@ -188,6 +192,37 @@
}
@Test
+ public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() {
+ @SuppressWarnings("unchecked")
+ final List<GroupedRecentTaskInfo>[] recentTasks = new List[1];
+ Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument;
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ setRawList(t1, t2, t3, t4, t5, t6);
+
+ // Mark a couple pairs [t2, t4], [t3, t5]
+ SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
+ SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+
+ mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
+ mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
+
+ mRecentTasksController.asRecentTasks()
+ .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer);
+ mMainExecutor.flushAll();
+
+ assertGroupedTasksListEquals(recentTasks[0],
+ t1.taskId, -1,
+ t2.taskId, t4.taskId,
+ t3.taskId, t5.taskId,
+ t6.taskId, -1);
+ }
+
+ @Test
public void testGetRecentTasks_groupActiveFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).startMocking();
@@ -296,7 +331,7 @@
for (ActivityManager.RecentTaskInfo task : tasks) {
rawList.add(task);
}
- doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(),
+ doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
anyInt());
return rawList;
}
@@ -307,7 +342,7 @@
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
- private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks,
+ private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks,
int... expectedTaskIds) {
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b11e542..29f3773 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -521,6 +521,7 @@
"Interpolator.cpp",
"LightingInfo.cpp",
"Matrix.cpp",
+ "MemoryPolicy.cpp",
"PathParser.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 0759471..f06fa24 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -93,12 +93,14 @@
case ADATASPACE_SCRGB:
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
break;
+ default:
+ ALOGW("Unknown dataspace %d", dataspace);
+ // Treat unknown dataspaces as sRGB, so fall through
+ [[fallthrough]];
case ADATASPACE_SRGB:
// when sRGB is returned, it means wide color gamut is not supported.
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
break;
- default:
- LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
}
}
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 7291cab..b7e9999 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -42,6 +42,8 @@
namespace android::uirenderer {
+static constexpr auto kThreadTimeout = 60000_ms;
+
class AHBUploader;
// This helper uploader classes allows us to upload using either EGL or Vulkan using the same
// interface.
@@ -80,7 +82,7 @@
}
void postIdleTimeoutCheck() {
- mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
+ mUploadThread->queue().postDelayed(kThreadTimeout, [this]() { this->idleTimeoutCheck(); });
}
protected:
@@ -97,7 +99,7 @@
bool shouldTimeOutLocked() {
nsecs_t durationSince = systemTime() - mLastUpload;
- return durationSince > 2000_ms;
+ return durationSince > kThreadTimeout;
}
void idleTimeoutCheck() {
diff --git a/libs/hwui/MemoryPolicy.cpp b/libs/hwui/MemoryPolicy.cpp
new file mode 100644
index 0000000..ca1312e7
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "MemoryPolicy.h"
+
+#include <android-base/properties.h>
+
+#include <optional>
+#include <string_view>
+
+#include "Properties.h"
+
+namespace android::uirenderer {
+
+constexpr static MemoryPolicy sDefaultMemoryPolicy;
+constexpr static MemoryPolicy sPersistentOrSystemPolicy{
+ .contextTimeout = 10_s,
+ .useAlternativeUiHidden = true,
+};
+constexpr static MemoryPolicy sLowRamPolicy{
+ .useAlternativeUiHidden = true,
+ .purgeScratchOnly = false,
+};
+constexpr static MemoryPolicy sExtremeLowRam{
+ .initialMaxSurfaceAreaScale = 0.2f,
+ .surfaceSizeMultiplier = 5 * 4.0f,
+ .backgroundRetentionPercent = 0.2f,
+ .contextTimeout = 5_s,
+ .minimumResourceRetention = 1_s,
+ .useAlternativeUiHidden = true,
+ .purgeScratchOnly = false,
+ .releaseContextOnStoppedOnly = true,
+};
+
+const MemoryPolicy& loadMemoryPolicy() {
+ if (Properties::isSystemOrPersistent) {
+ return sPersistentOrSystemPolicy;
+ }
+ std::string memoryPolicy = base::GetProperty(PROPERTY_MEMORY_POLICY, "");
+ if (memoryPolicy == "default") {
+ return sDefaultMemoryPolicy;
+ }
+ if (memoryPolicy == "lowram") {
+ return sLowRamPolicy;
+ }
+ if (memoryPolicy == "extremelowram") {
+ return sExtremeLowRam;
+ }
+
+ if (Properties::isLowRam) {
+ return sLowRamPolicy;
+ }
+ return sDefaultMemoryPolicy;
+}
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h
new file mode 100644
index 0000000..e86b338
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "utils/TimeUtils.h"
+
+namespace android::uirenderer {
+
+// Values mirror those from ComponentCallbacks2.java
+enum class TrimLevel {
+ COMPLETE = 80,
+ MODERATE = 60,
+ BACKGROUND = 40,
+ UI_HIDDEN = 20,
+ RUNNING_CRITICAL = 15,
+ RUNNING_LOW = 10,
+ RUNNING_MODERATE = 5,
+};
+
+struct MemoryPolicy {
+ // The initial scale factor applied to the display resolution. The default is 1, but
+ // lower values may be used to start with a smaller initial cache size. The cache will
+ // be adjusted if larger frames are actually rendered
+ float initialMaxSurfaceAreaScale = 1.0f;
+ // The foreground cache size multiplier. The surface area of the screen will be multiplied
+ // by this
+ float surfaceSizeMultiplier = 12.0f * 4.0f;
+ // How much of the foreground cache size should be preserved when going into the background
+ float backgroundRetentionPercent = 0.5f;
+ // How long after the last renderer goes away before the GPU context is released. A value
+ // of 0 means only drop the context on background TRIM signals
+ nsecs_t contextTimeout = 0_ms;
+ // The minimum amount of time to hold onto items in the resource cache
+ // The actual time used will be the max of this & when frames were actually rendered
+ nsecs_t minimumResourceRetention = 10_s;
+ // If false, use only TRIM_UI_HIDDEN to drive background cache limits;
+ // If true, use all signals (such as all contexts are stopped) to drive the limits
+ bool useAlternativeUiHidden = false;
+ // Whether or not to only purge scratch resources when triggering UI Hidden or background
+ // collection
+ bool purgeScratchOnly = true;
+ // EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped
+ bool releaseContextOnStoppedOnly = false;
+};
+
+const MemoryPolicy& loadMemoryPolicy();
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 5a67eb9..277955e 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -87,6 +87,10 @@
bool Properties::enableWebViewOverlays = true;
+bool Properties::isHighEndGfx = true;
+bool Properties::isLowRam = false;
+bool Properties::isSystemOrPersistent = false;
+
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2f8c679..96a5176 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -193,6 +193,8 @@
*/
#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
+#define PROPERTY_MEMORY_POLICY "debug.hwui.app_memory_policy"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -292,16 +294,27 @@
static bool enableWebViewOverlays;
+ static bool isHighEndGfx;
+ static bool isLowRam;
+ static bool isSystemOrPersistent;
+
static StretchEffectBehavior getStretchEffectBehavior() {
return stretchEffectBehavior;
}
static void setIsHighEndGfx(bool isHighEndGfx) {
+ Properties::isHighEndGfx = isHighEndGfx;
stretchEffectBehavior = isHighEndGfx ?
StretchEffectBehavior::ShaderHWUI :
StretchEffectBehavior::UniformScale;
}
+ static void setIsLowRam(bool isLowRam) { Properties::isLowRam = isLowRam; }
+
+ static void setIsSystemOrPersistent(bool isSystemOrPersistent) {
+ Properties::isSystemOrPersistent = isSystemOrPersistent;
+ }
+
/**
* Used for testing. Typical configuration of stretch behavior is done
* through setIsHighEndGfx
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 4f281fc..704fba9 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -271,6 +271,16 @@
Properties::setIsHighEndGfx(jIsHighEndGfx);
}
+static void android_view_ThreadedRenderer_setIsLowRam(JNIEnv* env, jobject clazz,
+ jboolean isLowRam) {
+ Properties::setIsLowRam(isLowRam);
+}
+
+static void android_view_ThreadedRenderer_setIsSystemOrPersistent(JNIEnv* env, jobject clazz,
+ jboolean isSystemOrPersistent) {
+ Properties::setIsSystemOrPersistent(isSystemOrPersistent);
+}
+
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo,
jint frameInfoSize) {
@@ -949,6 +959,9 @@
{"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
{"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint},
{"nSetIsHighEndGfx", "(Z)V", (void*)android_view_ThreadedRenderer_setIsHighEndGfx},
+ {"nSetIsLowRam", "(Z)V", (void*)android_view_ThreadedRenderer_setIsLowRam},
+ {"nSetIsSystemOrPersistent", "(Z)V",
+ (void*)android_view_ThreadedRenderer_setIsSystemOrPersistent},
{"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
{"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
{"nRegisterAnimatingRenderNode", "(JJ)V",
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index ded2b06..1d24e71 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -16,6 +16,16 @@
#include "CacheManager.h"
+#include <GrContextOptions.h>
+#include <SkExecutor.h>
+#include <SkGraphics.h>
+#include <SkMathPriv.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+#include <set>
+
+#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "Layer.h"
#include "Properties.h"
@@ -25,40 +35,34 @@
#include "pipeline/skia/SkiaMemoryTracer.h"
#include "renderstate/RenderState.h"
#include "thread/CommonPool.h"
-#include <utils/Trace.h>
-
-#include <GrContextOptions.h>
-#include <SkExecutor.h>
-#include <SkGraphics.h>
-#include <SkMathPriv.h>
-#include <math.h>
-#include <set>
namespace android {
namespace uirenderer {
namespace renderthread {
-// This multiplier was selected based on historical review of cache sizes relative
-// to the screen resolution. This is meant to be a conservative default based on
-// that analysis. The 4.0f is used because the default pixel format is assumed to
-// be ARGB_8888.
-#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
-#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
+CacheManager::CacheManager(RenderThread& thread)
+ : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {
+ mMaxSurfaceArea = static_cast<size_t>((DeviceInfo::getWidth() * DeviceInfo::getHeight()) *
+ mMemoryPolicy.initialMaxSurfaceAreaScale);
+ setupCacheLimits();
+}
-CacheManager::CacheManager()
- : mMaxSurfaceArea(DeviceInfo::getWidth() * DeviceInfo::getHeight())
- , mMaxResourceBytes(mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER)
- , mBackgroundResourceBytes(mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE)
- // This sets the maximum size for a single texture atlas in the GPU font cache. If
- // necessary, the cache can allocate additional textures that are counted against the
- // total cache limits provided to Skia.
- , mMaxGpuFontAtlasBytes(GrNextSizePow2(mMaxSurfaceArea))
- // This sets the maximum size of the CPU font cache to be at least the same size as the
- // total number of GPU font caches (i.e. 4 separate GPU atlases).
- , mMaxCpuFontCacheBytes(
- std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit()))
- , mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) {
+void CacheManager::setupCacheLimits() {
+ mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
+ mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
+ // This sets the maximum size for a single texture atlas in the GPU font cache. If
+ // necessary, the cache can allocate additional textures that are counted against the
+ // total cache limits provided to Skia.
+ mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
+ // This sets the maximum size of the CPU font cache to be at least the same size as the
+ // total number of GPU font caches (i.e. 4 separate GPU atlases).
+ mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
+ mBackgroundCpuFontCacheBytes = mMaxCpuFontCacheBytes * mMemoryPolicy.backgroundRetentionPercent;
+
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
+ if (mGrContext) {
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+ }
}
void CacheManager::reset(sk_sp<GrDirectContext> context) {
@@ -69,6 +73,7 @@
if (context) {
mGrContext = std::move(context);
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+ mLastDeferredCleanup = systemTime(CLOCK_MONOTONIC);
}
}
@@ -96,7 +101,7 @@
contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
-void CacheManager::trimMemory(TrimMemoryMode mode) {
+void CacheManager::trimMemory(TrimLevel mode) {
if (!mGrContext) {
return;
}
@@ -104,21 +109,28 @@
// flush and submit all work to the gpu and wait for it to finish
mGrContext->flushAndSubmit(/*syncCpu=*/true);
+ if (!Properties::isHighEndGfx && mode >= TrimLevel::MODERATE) {
+ mode = TrimLevel::COMPLETE;
+ }
+
switch (mode) {
- case TrimMemoryMode::Complete:
+ case TrimLevel::COMPLETE:
mGrContext->freeGpuResources();
SkGraphics::PurgeAllCaches();
+ mRenderThread.destroyRenderingContext();
break;
- case TrimMemoryMode::UiHidden:
+ case TrimLevel::UI_HIDDEN:
// Here we purge all the unlocked scratch resources and then toggle the resources cache
// limits between the background and max amounts. This causes the unlocked resources
// that have persistent data to be purged in LRU order.
- mGrContext->purgeUnlockedResources(true);
mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
- mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
+ mGrContext->purgeUnlockedResources(mMemoryPolicy.purgeScratchOnly);
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
break;
+ default:
+ break;
}
}
@@ -147,11 +159,29 @@
}
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
+ log.appendFormat(R"(Memory policy:
+ Max surface area: %zu
+ Max resource usage: %.2fMB (x%.0f)
+ Background retention: %.0f%% (altUiHidden = %s)
+)",
+ mMaxSurfaceArea, mMaxResourceBytes / 1000000.f,
+ mMemoryPolicy.surfaceSizeMultiplier,
+ mMemoryPolicy.backgroundRetentionPercent * 100.0f,
+ mMemoryPolicy.useAlternativeUiHidden ? "true" : "false");
+ if (Properties::isSystemOrPersistent) {
+ log.appendFormat(" IsSystemOrPersistent\n");
+ }
+ log.appendFormat(" GPU Context timeout: %" PRIu64 "\n", ns2s(mMemoryPolicy.contextTimeout));
+ size_t stoppedContexts = 0;
+ for (auto context : mCanvasContexts) {
+ if (context->isStopped()) stoppedContexts++;
+ }
+ log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts);
+
if (!mGrContext) {
- log.appendFormat("No valid cache instance.\n");
+ log.appendFormat("No GPU context.\n");
return;
}
-
std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
{"skia/sk_resource_cache/bitmap_", "Bitmaps"},
{"skia/sk_resource_cache/rrect-blur_", "Masks"},
@@ -199,6 +229,8 @@
}
void CacheManager::onFrameCompleted() {
+ cancelDestroyContext();
+ mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC);
if (ATRACE_ENABLED()) {
static skiapipeline::ATraceMemoryDump tracer;
tracer.startFrame();
@@ -210,11 +242,82 @@
}
}
-void CacheManager::performDeferredCleanup(nsecs_t cleanupOlderThanMillis) {
- if (mGrContext) {
- mGrContext->performDeferredCleanup(
- std::chrono::milliseconds(cleanupOlderThanMillis),
- /* scratchResourcesOnly */true);
+void CacheManager::onThreadIdle() {
+ if (!mGrContext || mFrameCompletions.size() == 0) return;
+
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ // Rate limiting
+ if ((now - mLastDeferredCleanup) < 25_ms) {
+ mLastDeferredCleanup = now;
+ const nsecs_t frameCompleteNanos = mFrameCompletions[0];
+ const nsecs_t frameDiffNanos = now - frameCompleteNanos;
+ const nsecs_t cleanupMillis =
+ ns2ms(std::max(frameDiffNanos, mMemoryPolicy.minimumResourceRetention));
+ mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis),
+ mMemoryPolicy.purgeScratchOnly);
+ }
+}
+
+void CacheManager::scheduleDestroyContext() {
+ if (mMemoryPolicy.contextTimeout > 0) {
+ mRenderThread.queue().postDelayed(mMemoryPolicy.contextTimeout,
+ [this, genId = mGenerationId] {
+ if (mGenerationId != genId) return;
+ // GenID should have already stopped this, but just in
+ // case
+ if (!areAllContextsStopped()) return;
+ mRenderThread.destroyRenderingContext();
+ });
+ }
+}
+
+void CacheManager::cancelDestroyContext() {
+ if (mIsDestructionPending) {
+ mIsDestructionPending = false;
+ mGenerationId++;
+ }
+}
+
+bool CacheManager::areAllContextsStopped() {
+ for (auto context : mCanvasContexts) {
+ if (!context->isStopped()) return false;
+ }
+ return true;
+}
+
+void CacheManager::checkUiHidden() {
+ if (!mGrContext) return;
+
+ if (mMemoryPolicy.useAlternativeUiHidden && areAllContextsStopped()) {
+ trimMemory(TrimLevel::UI_HIDDEN);
+ }
+}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {
+ mCanvasContexts.push_back(context);
+ cancelDestroyContext();
+}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {
+ std::erase(mCanvasContexts, context);
+ checkUiHidden();
+ if (mCanvasContexts.empty()) {
+ scheduleDestroyContext();
+ }
+}
+
+void CacheManager::onContextStopped(CanvasContext* context) {
+ checkUiHidden();
+ if (mMemoryPolicy.releaseContextOnStoppedOnly && areAllContextsStopped()) {
+ scheduleDestroyContext();
+ }
+}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {
+ int frameArea = width * height;
+ if (frameArea > mMaxSurfaceArea) {
+ mMaxSurfaceArea = frameArea;
+ setupCacheLimits();
}
}
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index af82672..d21ac9b 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -22,7 +22,11 @@
#endif
#include <SkSurface.h>
#include <utils/String8.h>
+
#include <vector>
+
+#include "MemoryPolicy.h"
+#include "utils/RingBuffer.h"
#include "utils/TimeUtils.h"
namespace android {
@@ -35,17 +39,15 @@
namespace renderthread {
-class IRenderPipeline;
class RenderThread;
+class CanvasContext;
class CacheManager {
public:
- enum class TrimMemoryMode { Complete, UiHidden };
-
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
#endif
- void trimMemory(TrimMemoryMode mode);
+ void trimMemory(TrimLevel mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
@@ -53,30 +55,50 @@
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
void onFrameCompleted();
+ void notifyNextFrameSize(int width, int height);
- void performDeferredCleanup(nsecs_t cleanupOlderThanMillis);
+ void onThreadIdle();
+
+ void registerCanvasContext(CanvasContext* context);
+ void unregisterCanvasContext(CanvasContext* context);
+ void onContextStopped(CanvasContext* context);
private:
friend class RenderThread;
- explicit CacheManager();
+ explicit CacheManager(RenderThread& thread);
+ void setupCacheLimits();
+ bool areAllContextsStopped();
+ void checkUiHidden();
+ void scheduleDestroyContext();
+ void cancelDestroyContext();
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
void reset(sk_sp<GrDirectContext> grContext);
#endif
void destroy();
- const size_t mMaxSurfaceArea;
+ RenderThread& mRenderThread;
+ const MemoryPolicy& mMemoryPolicy;
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
sk_sp<GrDirectContext> mGrContext;
#endif
- const size_t mMaxResourceBytes;
- const size_t mBackgroundResourceBytes;
+ size_t mMaxSurfaceArea = 0;
- const size_t mMaxGpuFontAtlasBytes;
- const size_t mMaxCpuFontCacheBytes;
- const size_t mBackgroundCpuFontCacheBytes;
+ size_t mMaxResourceBytes = 0;
+ size_t mBackgroundResourceBytes = 0;
+
+ size_t mMaxGpuFontAtlasBytes = 0;
+ size_t mMaxCpuFontCacheBytes = 0;
+ size_t mBackgroundCpuFontCacheBytes = 0;
+
+ std::vector<CanvasContext*> mCanvasContexts;
+ RingBuffer<uint64_t, 100> mFrameCompletions;
+
+ nsecs_t mLastDeferredCleanup = 0;
+ bool mIsDestructionPending = false;
+ uint32_t mGenerationId = 0;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 75d3ff7..6a0c5a8 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -42,9 +42,6 @@
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
-#define TRIM_MEMORY_COMPLETE 80
-#define TRIM_MEMORY_UI_HIDDEN 20
-
#define LOG_FRAMETIME_MMA 0
#if LOG_FRAMETIME_MMA
@@ -122,6 +119,7 @@
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline)) {
+ mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
@@ -133,6 +131,7 @@
node->clearRoot();
}
mRenderNodes.clear();
+ mRenderThread.cacheManager().unregisterCanvasContext(this);
}
void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
@@ -154,6 +153,7 @@
freePrefetchedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
+ mRenderThread.cacheManager().onContextStopped(this);
}
static void setBufferCount(ANativeWindow* window) {
@@ -251,6 +251,7 @@
mGenerationID++;
mRenderThread.removeFrameCallback(this);
mRenderPipeline->onStop();
+ mRenderThread.cacheManager().onContextStopped(this);
} else if (mIsDirty && hasSurface()) {
mRenderThread.postFrameCallback(this);
}
@@ -461,7 +462,6 @@
}
void CanvasContext::stopDrawing() {
- cleanupResources();
mRenderThread.removeFrameCallback(this);
mAnimationContext->pauseAnimators();
mGenerationID++;
@@ -648,25 +648,10 @@
}
}
- cleanupResources();
mRenderThread.cacheManager().onFrameCompleted();
return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
}
-void CanvasContext::cleanupResources() {
- auto& tracker = mJankTracker.frames();
- auto size = tracker.size();
- auto capacity = tracker.capacity();
- if (size == capacity) {
- nsecs_t nowNanos = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t frameCompleteNanos =
- tracker[0].get(FrameInfoIndex::FrameCompleted);
- nsecs_t frameDiffNanos = nowNanos - frameCompleteNanos;
- nsecs_t cleanupMillis = ns2ms(std::max(frameDiffNanos, 10_s));
- mRenderThread.cacheManager().performDeferredCleanup(cleanupMillis);
- }
-}
-
void CanvasContext::reportMetricsWithPresentTime() {
{ // acquire lock
std::scoped_lock lock(mFrameMetricsReporterMutex);
@@ -790,6 +775,7 @@
SkISize size;
size.fWidth = ANativeWindow_getWidth(anw);
size.fHeight = ANativeWindow_getHeight(anw);
+ mRenderThread.cacheManager().notifyNextFrameSize(size.fWidth, size.fHeight);
return size;
}
@@ -868,18 +854,6 @@
}
}
-void CanvasContext::trimMemory(RenderThread& thread, int level) {
- ATRACE_CALL();
- if (!thread.getGrContext()) return;
- ATRACE_CALL();
- if (level >= TRIM_MEMORY_COMPLETE) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
- thread.destroyRenderingContext();
- } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
- }
-}
-
DeferredLayerUpdater* CanvasContext::createTextureLayer() {
return mRenderPipeline->createTextureLayer();
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 951ee21..748ab96 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,6 +127,7 @@
void setSurfaceControl(ASurfaceControl* surfaceControl);
bool pauseSurface();
void setStopped(bool stopped);
+ bool isStopped() { return mStopped || !hasSurface(); }
bool hasSurface() const { return mNativeSurface.get(); }
void allocateBuffers();
@@ -148,7 +149,6 @@
void markLayerInUse(RenderNode* node);
void destroyHardwareResources();
- static void trimMemory(RenderThread& thread, int level);
DeferredLayerUpdater* createTextureLayer();
@@ -330,8 +330,6 @@
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
-
- void cleanupResources();
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 59c914f..03f02de 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -243,7 +243,9 @@
mContext->unpinImages();
for (size_t i = 0; i < mLayers.size(); i++) {
- mLayers[i]->apply();
+ if (mLayers[i]) {
+ mLayers[i]->apply();
+ }
}
mLayers.clear();
mContext->setContentDrawBounds(mContentDrawBounds);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 40a0bac..3324715 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -196,7 +196,8 @@
// Avoid creating a RenderThread to do a trimMemory.
if (RenderThread::hasInstance()) {
RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); });
+ const auto trimLevel = static_cast<TrimLevel>(level);
+ thread.queue().post([&thread, trimLevel]() { thread.trimMemory(trimLevel); });
}
}
@@ -205,7 +206,7 @@
RenderThread& thread = RenderThread::getInstance();
thread.queue().post([&thread]() {
if (thread.getGrContext()) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ thread.cacheManager().trimMemory(TrimLevel::COMPLETE);
}
});
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3ff4081..7a7f1ab 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -251,7 +251,7 @@
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = VulkanManager::getInstance();
- mCacheManager = new CacheManager();
+ mCacheManager = new CacheManager(*this);
}
void RenderThread::setupFrameInterval() {
@@ -453,6 +453,8 @@
// next vsync (oops), so none of the callbacks are run.
requestVsync();
}
+
+ mCacheManager->onThreadIdle();
}
return false;
@@ -502,6 +504,11 @@
HardwareBitmapUploader::initialize();
}
+void RenderThread::trimMemory(TrimLevel level) {
+ ATRACE_CALL();
+ cacheManager().trimMemory(level);
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index c1f6790..0a89e5e 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,11 +17,11 @@
#ifndef RENDERTHREAD_H_
#define RENDERTHREAD_H_
-#include <surface_control_private.h>
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
#include <private/android/choreographer.h>
+#include <surface_control_private.h>
#include <thread/ThreadBase.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
@@ -31,6 +31,7 @@
#include <set>
#include "CacheManager.h"
+#include "MemoryPolicy.h"
#include "ProfileDataContainer.h"
#include "RenderTask.h"
#include "TimeLord.h"
@@ -172,6 +173,8 @@
return mASurfaceControlFunctions;
}
+ void trimMemory(TrimLevel level);
+
/**
* isCurrent provides a way to query, if the caller is running on
* the render thread.
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index edd3e4e..df06ead 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -58,7 +58,7 @@
ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
// attempt to trim all memory while we still hold strong refs
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
// free the surfaces
@@ -75,11 +75,11 @@
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
// UI hidden and make sure only some got purged (unique should remain)
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
+ renderThread.cacheManager().trimMemory(TrimLevel::UI_HIDDEN);
ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
// complete and make sure all get purged
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index be9862b..49b314d 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -242,13 +242,14 @@
* To decode such an image, {@link MediaCodec} decoder for
* {@link #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
* the correct {@link #MediaFormat} based on additional information in
- * the track format, and send it to {@link MediaCodec#configure}.
+ * the track format (shown in the next paragraph), and send it to
+ * {@link MediaCodec#configure}.
*
* The track's MediaFormat will come with {@link #KEY_WIDTH} and
* {@link #KEY_HEIGHT} keys, which describes the width and height
* of the image. If the image doesn't contain grid (i.e. none of
* {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
- * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present}), the
+ * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present), the
* track will contain a single sample of coded data for the entire image,
* and the image width and height should be used to set up the decoder.
*
@@ -266,6 +267,36 @@
public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
/**
+ * MIME type for AVIF still image data encoded in AV1.
+ *
+ * To decode such an image, {@link MediaCodec} decoder for
+ * {@link #MIMETYPE_VIDEO_AV1} shall be used. The client needs to form
+ * the correct {@link #MediaFormat} based on additional information in
+ * the track format (shown in the next paragraph), and send it to
+ * {@link MediaCodec#configure}.
+ *
+ * The track's MediaFormat will come with {@link #KEY_WIDTH} and
+ * {@link #KEY_HEIGHT} keys, which describes the width and height
+ * of the image. If the image doesn't contain grid (i.e. none of
+ * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
+ * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present), the
+ * track will contain a single sample of coded data for the entire image,
+ * and the image width and height should be used to set up the decoder.
+ *
+ * If the image does come with grid, each sample from the track will
+ * contain one tile in the grid, of which the size is described by
+ * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_HEIGHT}. This size
+ * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be
+ * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS}
+ * by {@link #KEY_GRID_COLUMNS} samples in row-major, top-row first,
+ * left-to-right order. The output image should be reconstructed by
+ * first tiling the decoding results of the tiles in the correct order,
+ * then trimming (before rotation is applied) on the bottom and right
+ * side, if the tiled area is larger than the image width and height.
+ */
+ public static final String MIMETYPE_IMAGE_AVIF = "image/avif";
+
+ /**
* MIME type for WebVTT subtitle data.
*/
public static final String MIMETYPE_TEXT_VTT = "text/vtt";
@@ -485,9 +516,11 @@
/**
* A key describing the width (in pixels) of each tile of the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_HEIGHT
* @see #KEY_GRID_ROWS
@@ -497,9 +530,11 @@
/**
* A key describing the height (in pixels) of each tile of the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_WIDTH
* @see #KEY_GRID_ROWS
@@ -509,9 +544,11 @@
/**
* A key describing the number of grid rows in the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_WIDTH
* @see #KEY_TILE_HEIGHT
@@ -521,9 +558,11 @@
/**
* A key describing the number of grid columns in the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_WIDTH
* @see #KEY_TILE_HEIGHT
@@ -1350,9 +1389,9 @@
* selected in the absence of a specific user choice.
* This is currently used in two scenarios:
* 1) for subtitle tracks, when the user selected 'Default' for the captioning locale.
- * 2) for a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track, indicating the image is the
- * primary item in the file.
-
+ * 2) for a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track,
+ * indicating the image is the primary item in the file.
+ *
* The associated value is an integer, where non-0 means TRUE. This is an optional
* field; if not specified, DEFAULT is considered to be FALSE.
*/
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index f8667ed..6384cad 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -22,7 +22,7 @@
}
}
plugins {
- id 'com.android.application' version '7.3.0-rc01' apply false
- id 'com.android.library' version '7.3.0-rc01' apply false
+ id 'com.android.application' version '7.3.0' apply false
+ id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
index 1ebc5da..332d5a8 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -18,8 +18,4 @@
import com.android.settingslib.spa.framework.DebugActivity
-class GalleryDebugActivity : DebugActivity(
- SpaEnvironment.EntryRepository,
- browseActivityClass = MainActivity::class.java,
- entryProviderAuthorities = "com.android.spa.gallery.provider",
-)
+class GalleryDebugActivity : DebugActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
index d3e0096..5e04861 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
@@ -18,7 +18,4 @@
import com.android.settingslib.spa.framework.EntryProvider
-class GalleryEntryProvider : EntryProvider(
- SpaEnvironment.EntryRepository,
- browseActivityClass = MainActivity::class.java,
-)
+class GalleryEntryProvider : EntryProvider(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
similarity index 89%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 6fe88e1..33c4d77 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -18,9 +18,9 @@
import android.os.Bundle
import androidx.navigation.NamedNavArgument
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
@@ -62,9 +62,8 @@
)
}
-object SpaEnvironment {
- val PageProviderRepository: SettingsPageProviderRepository by
- lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
+object GallerySpaEnvironment : SpaEnvironment() {
+ override val pageProviderRepository = lazy {
SettingsPageProviderRepository(
allPageProviders = listOf(
HomePageProvider,
@@ -88,9 +87,7 @@
)
}
- val EntryRepository: SettingsEntryRepository by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- SettingsEntryRepository(PageProviderRepository)
- }
+ override val browseActivityClass = MainActivity::class.java
- // TODO: add other environment setup here.
+ override val entryProviderAuthorities = "com.android.spa.gallery.provider"
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
index a063847..5e859ce 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
@@ -18,4 +18,4 @@
import com.android.settingslib.spa.framework.BrowseActivity
-class MainActivity : BrowseActivity(SpaEnvironment.PageProviderRepository)
+class MainActivity : BrowseActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 0bb631a..138ea02 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -31,7 +31,7 @@
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.navRoute
@@ -48,9 +48,9 @@
* $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination HOME
* $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination ARGUMENT/bar/5
*/
-open class BrowseActivity(
- private val sppRepository: SettingsPageProviderRepository,
-) : ComponentActivity() {
+open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity() {
+ private val sppRepository by spaEnvironment.pageProviderRepository
+
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib_DayNight)
super.onCreate(savedInstanceState)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
index c698d9c..85fc366 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -35,8 +35,8 @@
import com.android.settingslib.spa.R
import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_DESTINATION
import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.toState
@@ -61,11 +61,9 @@
* For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
* For SettingsGoogle, Activity = com.android.settings/.spa.SpaDebugActivity
*/
-open class DebugActivity(
- private val entryRepository: SettingsEntryRepository,
- private val browseActivityClass: Class<*>,
- private val entryProviderAuthorities: String? = null,
-) : ComponentActivity() {
+open class DebugActivity(private val spaEnvironment: SpaEnvironment) : ComponentActivity() {
+ private val entryRepository by spaEnvironment.entryRepository
+
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib_DayNight)
super.onCreate(savedInstanceState)
@@ -79,7 +77,7 @@
}
private fun displayDebugMessage() {
- if (entryProviderAuthorities == null) return
+ val entryProviderAuthorities = spaEnvironment.entryProviderAuthorities ?: return
try {
val query = EntryProvider.QueryEnum.PAGE_INFO_QUERY
@@ -216,7 +214,7 @@
if (page.hasRuntimeParam()) return null
val context = LocalContext.current
val route = page.buildRoute()
- val intent = Intent(context, browseActivityClass).apply {
+ val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
}
return {
@@ -230,7 +228,7 @@
if (entry.hasRuntimeParam()) return null
val context = LocalContext.current
val route = entry.buildRoute()
- val intent = Intent(context, browseActivityClass).apply {
+ val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
}
return {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
index d923c1c..f0ec83b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
@@ -28,8 +28,8 @@
import android.database.MatrixCursor
import android.net.Uri
import android.util.Log
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
/**
* The content provider to return entry related data, which can be used for search and hierarchy.
@@ -42,10 +42,9 @@
* $ adb shell content query --uri content://<AuthorityPath>/page_info
* $ adb shell content query --uri content://<AuthorityPath>/entry_info
*/
-open class EntryProvider(
- private val entryRepository: SettingsEntryRepository,
- private val browseActivityClass: Class<*>? = null,
-) : ContentProvider() {
+open class EntryProvider(spaEnvironment: SpaEnvironment) : ContentProvider() {
+ private val entryRepository by spaEnvironment.entryRepository
+ private val browseActivityClass = spaEnvironment.browseActivityClass
/**
* Enum to define all column names in provider.
@@ -220,7 +219,7 @@
}
private fun createBrowsePageIntent(page: SettingsPage): Intent {
- if (context == null || browseActivityClass == null || page.hasRuntimeParam())
+ if (context == null || page.hasRuntimeParam())
return Intent()
return Intent().setComponent(ComponentName(context!!, browseActivityClass)).apply {
@@ -231,8 +230,7 @@
private fun createBrowsePageAdbCommand(page: SettingsPage): String? {
if (context == null || page.hasRuntimeParam()) return null
val packageName = context!!.packageName
- val activityName =
- browseActivityClass?.name?.replace(packageName, "") ?: "<browse-activity-class>"
+ val activityName = browseActivityClass.name.replace(packageName, "")
return "adb shell am start -n $packageName/$activityName" +
" -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
new file mode 100644
index 0000000..111555b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 com.android.settingslib.spa.framework.common
+
+import android.app.Activity
+
+abstract class SpaEnvironment {
+ abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
+
+ val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
+
+ abstract val browseActivityClass: Class<out Activity>
+
+ open val entryProviderAuthorities: String? = null
+
+ // TODO: add other environment setup here.
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 7927c5d..eb53ea1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -758,16 +758,23 @@
}
public boolean isBusy() {
- synchronized (mProfileLock) {
- for (LocalBluetoothProfile profile : mProfiles) {
- int status = getProfileConnectionState(profile);
- if (status == BluetoothProfile.STATE_CONNECTING
- || status == BluetoothProfile.STATE_DISCONNECTING) {
- return true;
- }
+ for (CachedBluetoothDevice memberDevice : getMemberDevice()) {
+ if (isBusyState(memberDevice)) {
+ return true;
}
- return getBondState() == BluetoothDevice.BOND_BONDING;
}
+ return isBusyState(this);
+ }
+
+ private boolean isBusyState(CachedBluetoothDevice device){
+ for (LocalBluetoothProfile profile : device.getProfiles()) {
+ int status = device.getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTING
+ || status == BluetoothProfile.STATE_DISCONNECTING) {
+ return true;
+ }
+ }
+ return device.getBondState() == BluetoothDevice.BOND_BONDING;
}
private boolean updateProfiles() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 79e9938..315ab0a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1069,4 +1069,80 @@
assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
}
+
+ @Test
+ public void isBusy_mainDeviceIsConnecting_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_mainDeviceIsBonding_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_memberDeviceIsConnecting_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_memberDeviceIsBonding_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_allDevicesAreNotBusy_returnsNotBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isFalse();
+ }
}
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 234ef24..aaee42f 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,25 +1,6 @@
{
// Looking for unit test presubmit configuration?
// This currently lives in ATP config apct/system_ui/unit_test
- "presubmit-large": [
- {
- "name": "PlatformScenarioTests",
- "options": [
- {
- "include-filter": "android.platform.test.scenario.sysui"
- },
- {
- "include-annotation": "android.platform.test.scenario.annotation.Scenario"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ]
- }
- ],
"presubmit-sysui": [
{
"name": "PlatformScenarioTests",
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index f1e54e0..2cd564f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -149,32 +149,6 @@
}
}
}
-
- companion object {
- private const val SQUISHINESS_SCALE_START = 0.5
- private const val SQUISHINESS_SCALE_FACTOR = 0.5
- private fun getSquishinessScale(squishinessFraction: Float): Double {
- return SQUISHINESS_SCALE_START + SQUISHINESS_SCALE_FACTOR * squishinessFraction
- }
- }
-
- var squishinessFraction: Float = 1f
- set(value) {
- if (field == value) {
- return
- }
- field = value
-
- val scale = getSquishinessScale(field)
- for (mediaPlayer in MediaPlayerData.players()) {
- mediaPlayer.mediaViewHolder?.let {
- it.player.bottom = it.player.top + (scale * it.player.measuredHeight).toInt()
- } ?: mediaPlayer.recommendationViewHolder?.let {
- it.recommendations.bottom = it.recommendations.top +
- (scale * it.recommendations.measuredHeight).toInt()
- }
- }
- }
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
// System font changes should only happen when UMO is offscreen or a flicker may occur
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index 59c6635..2b381a9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -42,7 +42,7 @@
}
private fun List<RecentTask>.sortTasks(): List<RecentTask> =
- asReversed().sortedBy {
+ sortedBy {
// Show normal tasks first and only then tasks with opened app selector
it.topActivityComponent == appSelectorComponentName
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
index 0bdddfe..0927f3b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
@@ -17,11 +17,17 @@
package com.android.systemui.mediaprojection.appselector.data
import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ComponentInfoFlags
import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import com.android.launcher3.icons.BaseIconFactory.IconOptions
+import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
-import javax.inject.Inject
interface AppIconLoader {
suspend fun loadIcon(userId: Int, component: ComponentName): Drawable?
@@ -31,11 +37,20 @@
@Inject
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val context: Context,
+ private val packageManager: PackageManager
) : AppIconLoader {
override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
withContext(backgroundDispatcher) {
- // TODO(b/240924731): add a blocking call to load an icon using iconloaderlib
- null
+ IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
+ val activityInfo = packageManager
+ .getActivityInfo(component, ComponentInfoFlags.of(0))
+ val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
+ val userHandler = UserHandle.of(userId)
+ val options = IconOptions().apply { setUser(userHandler) }
+ val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
+ badgedIcon.newIcon(context)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index 6d67e28..cd994b8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -16,11 +16,13 @@
package com.android.systemui.mediaprojection.appselector.data
+import android.annotation.ColorInt
import android.content.ComponentName
data class RecentTask(
val taskId: Int,
val userId: Int,
val topActivityComponent: ComponentName?,
- val baseIntentComponent: ComponentName?
+ val baseIntentComponent: ComponentName?,
+ @ColorInt val colorBackground: Int?
)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 5a09435..e8b49cd 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -16,23 +16,61 @@
package com.android.systemui.mediaprojection.appselector.data
+import android.app.ActivityManager
+import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.getOrNull
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import java.util.Optional
+import javax.inject.Inject
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
-import javax.inject.Inject
+import java.util.concurrent.Executor
interface RecentTaskListProvider {
+ /** Loads recent tasks, the returned task list is from the most-recent to least-recent order */
suspend fun loadRecentTasks(): List<RecentTask>
}
class ShellRecentTaskListProvider
@Inject
-constructor(@Background private val coroutineDispatcher: CoroutineDispatcher) :
- RecentTaskListProvider {
+constructor(
+ @Background private val coroutineDispatcher: CoroutineDispatcher,
+ @Background private val backgroundExecutor: Executor,
+ private val recentTasks: Optional<RecentTasks>
+) : RecentTaskListProvider {
+
+ private val recents by lazy { recentTasks.getOrNull() }
override suspend fun loadRecentTasks(): List<RecentTask> =
withContext(coroutineDispatcher) {
- // TODO(b/240924731): add blocking call to load the recents
- emptyList()
+ val rawRecentTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
+
+ rawRecentTasks
+ .flatMap { listOfNotNull(it.taskInfo1, it.taskInfo2) }
+ .map {
+ RecentTask(
+ it.taskId,
+ it.userId,
+ it.topActivity,
+ it.baseIntent?.component,
+ it.taskDescription?.backgroundColor
+ )
+ }
+ }
+
+ private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
+ suspendCoroutine { continuation ->
+ getRecentTasks(
+ Integer.MAX_VALUE,
+ RECENT_IGNORE_UNAVAILABLE,
+ ActivityManager.getCurrentUser(),
+ backgroundExecutor
+ ) { tasks ->
+ continuation.resume(tasks)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
index 4291280..47faaed 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -30,12 +31,13 @@
@Inject
constructor(
@Background private val coroutineDispatcher: CoroutineDispatcher,
-) :
- RecentTaskThumbnailLoader {
+ private val activityManager: ActivityManagerWrapper
+) : RecentTaskThumbnailLoader {
override suspend fun loadThumbnail(taskId: Int): ThumbnailData? =
withContext(coroutineDispatcher) {
- // TODO(b/240924731): add blocking call to load a thumbnail
- null
+ val thumbnailData =
+ activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false)
+ if (thumbnailData.thumbnail == null) null else thumbnailData
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 30947e8..50a10bc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -541,10 +541,12 @@
if (!mImeVisible) {
// IME not showing, take all touches
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
}
if (!mView.isImeRenderingNavButtons()) {
// IME showing but not drawing any buttons, take all touches
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f41b905..18bd6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -27,7 +27,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostState;
@@ -76,14 +75,13 @@
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QS_PANEL) MediaHost mediaHost,
QSTileRevealController.Factory qsTileRevealControllerFactory,
- DumpManager dumpManager, MediaCarouselController mediaCarouselController,
- MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+ DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
FalsingManager falsingManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
- metricsLogger, uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
+ metricsLogger, uiEventLogger, qsLogger, dumpManager);
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index a5c60a4..ded466a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -32,7 +32,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -71,7 +70,6 @@
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
private final DumpManager mDumpManager;
- private final MediaCarouselController mMediaCarouselController;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
protected boolean mShouldUseSplitNotificationShade;
@@ -133,8 +131,7 @@
MetricsLogger metricsLogger,
UiEventLogger uiEventLogger,
QSLogger qsLogger,
- DumpManager dumpManager,
- MediaCarouselController mediaCarouselController
+ DumpManager dumpManager
) {
super(view);
mHost = host;
@@ -147,7 +144,6 @@
mDumpManager = dumpManager;
mShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
- mMediaCarouselController = mediaCarouselController;
}
@Override
@@ -165,7 +161,6 @@
public void setSquishinessFraction(float squishinessFraction) {
mView.setSquishinessFraction(squishinessFraction);
- mMediaCarouselController.setSquishinessFraction(squishinessFraction);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 7ce0ad0..9739974 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -26,7 +26,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
@@ -56,10 +55,10 @@
@Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
Provider<Boolean> usingCollapsedLandscapeMediaProvider,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, MediaCarouselController mediaCarouselController
+ DumpManager dumpManager
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
- uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
+ uiEventLogger, qsLogger, dumpManager);
mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index a8ad564..76f2dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -204,6 +204,7 @@
protected CentralSurfaces mCentralSurfaces;
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
+ private boolean mCentralSurfacesRegistered;
private View mNotificationContainer;
@@ -338,6 +339,7 @@
mNotificationContainer = notificationContainer;
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
centralSurfaces.getKeyguardMessageArea());
+ mCentralSurfacesRegistered = true;
registerListeners();
}
@@ -1086,6 +1088,9 @@
};
protected void updateStates() {
+ if (!mCentralSurfacesRegistered) {
+ return;
+ }
boolean showing = mShowing;
boolean occluded = mOccluded;
boolean bouncerShowing = bouncerIsShowing();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index e3e3b74..5ad3542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -46,7 +45,6 @@
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -73,10 +71,6 @@
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var logger: MediaUiEventLogger
@Mock lateinit var debugLogger: MediaCarouselControllerLogger
- @Mock lateinit var mediaViewHolder: MediaViewHolder
- @Mock lateinit var player: TransitionLayout
- @Mock lateinit var recommendationViewHolder: RecommendationViewHolder
- @Mock lateinit var recommendations: TransitionLayout
@Mock lateinit var mediaPlayer: MediaControlPanel
@Mock lateinit var mediaViewController: MediaViewController
@Mock lateinit var smartspaceMediaData: SmartspaceMediaData
@@ -282,46 +276,6 @@
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
- @Test
- fun testSetSquishinessFractionForMedia_setPlayerBottom() {
- whenever(panel.mediaViewHolder).thenReturn(mediaViewHolder)
- whenever(mediaViewHolder.player).thenReturn(player)
- whenever(player.measuredHeight).thenReturn(100)
-
- val playingLocal = Triple("playing local",
- DATA.copy(active = true, isPlaying = true,
- playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
- 4500L)
- MediaPlayerData.addMediaPlayer(playingLocal.first, playingLocal.second, panel, clock,
- false, debugLogger)
-
- mediaCarouselController.squishinessFraction = 0.0f
- verify(player).bottom = 50
- verifyNoMoreInteractions(recommendationViewHolder)
-
- mediaCarouselController.squishinessFraction = 0.5f
- verify(player).bottom = 75
- verifyNoMoreInteractions(recommendationViewHolder)
- }
-
- @Test
- fun testSetSquishinessFractionForRecommendation_setPlayerBottom() {
- whenever(panel.recommendationViewHolder).thenReturn(recommendationViewHolder)
- whenever(recommendationViewHolder.recommendations).thenReturn(recommendations)
- whenever(recommendations.measuredHeight).thenReturn(100)
-
- MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
- false, clock)
-
- mediaCarouselController.squishinessFraction = 0.0f
- verifyNoMoreInteractions(mediaViewHolder)
- verify(recommendationViewHolder.recommendations).bottom = 50
-
- mediaCarouselController.squishinessFraction = 0.5f
- verifyNoMoreInteractions(mediaViewHolder)
- verify(recommendationViewHolder.recommendations).bottom = 75
- }
-
fun testMediaLoaded_ScrollToActivePlayer() {
listener.value.onMediaDataLoaded("playing local",
null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 37b7f47..00b1f32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -54,7 +54,7 @@
}
@Test
- fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInReverse() {
+ fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
val tasks = listOf(
createRecentTask(taskId = 1),
createRecentTask(taskId = 2),
@@ -66,15 +66,15 @@
verify(view).bind(
listOf(
- createRecentTask(taskId = 3),
- createRecentTask(taskId = 2),
createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
)
)
}
@Test
- fun initRecentTasksWithAppSelectorTasks_bindsListInReverseAndAppSelectorTasksAtTheEnd() {
+ fun initRecentTasksWithAppSelectorTasks_bindsAppSelectorTasksAtTheEnd() {
val tasks = listOf(
createRecentTask(taskId = 1),
createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
@@ -88,11 +88,11 @@
verify(view).bind(
listOf(
- createRecentTask(taskId = 5),
- createRecentTask(taskId = 3),
createRecentTask(taskId = 1),
- createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
+ createRecentTask(taskId = 3),
+ createRecentTask(taskId = 5),
createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
+ createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
)
)
}
@@ -105,7 +105,8 @@
taskId = taskId,
topActivityComponent = topActivityComponent,
baseIntentComponent = ComponentName("com", "Test"),
- userId = 0
+ userId = 0,
+ colorBackground = 0
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
new file mode 100644
index 0000000..939af16
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -0,0 +1,112 @@
+package com.android.systemui.mediaprojection.appselector.data
+
+import android.app.ActivityManager.RecentTaskInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.google.common.truth.Truth.assertThat
+import java.util.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.function.Consumer
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ShellRecentTaskListProviderTest : SysuiTestCase() {
+
+ private val dispatcher = Dispatchers.Unconfined
+ private val recentTasks: RecentTasks = mock()
+ private val recentTaskListProvider =
+ ShellRecentTaskListProvider(dispatcher, Runnable::run, Optional.of(recentTasks))
+
+ @Test
+ fun loadRecentTasks_oneTask_returnsTheSameTask() {
+ givenRecentTasks(createSingleTask(taskId = 1))
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result).containsExactly(createRecentTask(taskId = 1))
+ }
+
+ @Test
+ fun loadRecentTasks_multipleTasks_returnsTheSameTasks() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createSingleTask(taskId = 2),
+ createSingleTask(taskId = 3),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ )
+ }
+
+ @Test
+ fun loadRecentTasks_groupedTask_returnsUngroupedTasks() {
+ givenRecentTasks(createTaskPair(taskId1 = 1, taskId2 = 2))
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(createRecentTask(taskId = 1), createRecentTask(taskId = 2))
+ }
+
+ @Test
+ fun loadRecentTasks_mixedSingleAndGroupedTask_returnsUngroupedTasks() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createTaskPair(taskId1 = 2, taskId2 = 3),
+ createSingleTask(taskId = 4),
+ createTaskPair(taskId1 = 5, taskId2 = 6),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ createRecentTask(taskId = 4),
+ createRecentTask(taskId = 5),
+ createRecentTask(taskId = 6),
+ )
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
+ whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
+ val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>>
+ consumer.accept(tasks.toList())
+ }
+ }
+
+ private fun createRecentTask(taskId: Int): RecentTask =
+ RecentTask(
+ taskId = taskId,
+ userId = 0,
+ topActivityComponent = null,
+ baseIntentComponent = null,
+ colorBackground = null
+ )
+
+ private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId))
+
+ private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null)
+
+ private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index cbe1186..3cad2a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -44,7 +44,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -87,7 +86,6 @@
@Mock
private QSLogger mQSLogger;
private DumpManager mDumpManager = new DumpManager();
- private MediaCarouselController mMediaCarouselController;
@Mock
QSTileImpl mQSTile;
@Mock
@@ -110,9 +108,9 @@
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, MediaCarouselController mediaCarouselController) {
+ DumpManager dumpManager) {
super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
- qsLogger, dumpManager, mediaCarouselController);
+ qsLogger, dumpManager);
}
@Override
@@ -146,7 +144,7 @@
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
mController.init();
reset(mQSTileRevealController);
@@ -158,7 +156,7 @@
QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
mQSTileHost, mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController) {
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
@Override
protected QSTileRevealController createTileRevealController() {
return mQSTileRevealController;
@@ -253,7 +251,7 @@
when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
@@ -262,7 +260,7 @@
when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 98d499a..5eb9a98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -6,7 +6,6 @@
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.MediaCarouselController
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.FalsingManager
@@ -41,7 +40,6 @@
@Mock private lateinit var qsCustomizerController: QSCustomizerController
@Mock private lateinit var qsTileRevealControllerFactory: QSTileRevealController.Factory
@Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock private lateinit var metricsLogger: MetricsLogger
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var qsLogger: QSLogger
@@ -78,7 +76,6 @@
mediaHost,
qsTileRevealControllerFactory,
dumpManager,
- mediaCarouselController,
metricsLogger,
uiEventLogger,
qsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 4af5b90..6af8e49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -23,7 +23,6 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.MediaCarouselController
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.qs.QSTile
@@ -60,7 +59,6 @@
@Mock private lateinit var tileLayout: TileLayout
@Mock private lateinit var tileView: QSTileView
@Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
- @Mock private lateinit var mediaCarouselController: MediaCarouselController
private val uiEventLogger = UiEventLoggerFake()
private val dumpManager = DumpManager()
@@ -90,8 +88,7 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager,
- mediaCarouselController)
+ dumpManager)
controller.init()
}
@@ -158,8 +155,7 @@
metricsLogger: MetricsLogger,
uiEventLogger: UiEventLoggerFake,
qsLogger: QSLogger,
- dumpManager: DumpManager,
- mediaCarouselController: MediaCarouselController
+ dumpManager: DumpManager
) :
QuickQSPanelController(
view,
@@ -171,8 +167,7 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager,
- mediaCarouselController) {
+ dumpManager) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index c47ea9c..6ace404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -275,10 +275,9 @@
EditText editText = view.findViewById(R.id.remote_input_text);
editText.setText(TEST_REPLY);
ClipDescription description = new ClipDescription("", new String[] {"image/png"});
- // We need to use an (arbitrary) real resource here so that an actual image gets attached.
+ // We need to use an (arbitrary) real resource here so that an actual image gets attached
ClipData clip = new ClipData(description, new ClipData.Item(
- Uri.parse("android.resource://com.android.systemui/"
- + R.drawable.default_thumbnail)));
+ Uri.parse("android.resource://android/" + android.R.drawable.btn_default)));
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
view.setAttachment(payload);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8d94f95..f17f8f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -731,7 +731,6 @@
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- intentFilter.addAction(Intent.ACTION_USER_PRESENT);
intentFilter.addAction(Intent.ACTION_SETTING_RESTORED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@@ -749,14 +748,6 @@
unlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- // We will update when the automation service dies.
- synchronized (mLock) {
- AccessibilityUserState userState = getCurrentUserStateLocked();
- if (readConfigurationForUserStateLocked(userState)) {
- onUserStateChangedLocked(userState);
- }
- }
} else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
final String which = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
if (Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(which)) {
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 838cbd9..ec30369b 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -24,7 +24,6 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -42,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 3efb628..f9b0dd0 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -137,6 +137,14 @@
private static final int DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS = 16;
/**
+ * For {@link BroadcastQueueModernImpl}: Maximum number of pending
+ * broadcasts to hold for a process before we ignore any delays that policy
+ * might have applied to that process.
+ */
+ public int MAX_PENDING_BROADCASTS = DEFAULT_MAX_PENDING_BROADCASTS;
+ private static final int DEFAULT_MAX_PENDING_BROADCASTS = 256;
+
+ /**
* For {@link BroadcastQueueModernImpl}: Default delay to apply to normal
* broadcasts, giving a chance for debouncing of rapidly changing events.
*/
@@ -217,6 +225,8 @@
DEFAULT_MAX_RUNNING_PROCESS_QUEUES);
MAX_RUNNING_ACTIVE_BROADCASTS = properties.getInt("bcast_max_running_active_broadcasts",
DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS);
+ MAX_PENDING_BROADCASTS = properties.getInt("bcast_max_pending_broadcasts",
+ DEFAULT_MAX_PENDING_BROADCASTS);
DELAY_NORMAL_MILLIS = properties.getLong("bcast_delay_normal_millis",
DEFAULT_DELAY_NORMAL_MILLIS);
DELAY_CACHED_MILLIS = properties.getLong("bcast_delay_cached_millis",
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 77eefb4..342d1f2 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -119,6 +119,9 @@
private boolean mProcessCached;
+ private String mCachedToString;
+ private String mCachedToShortString;
+
public BroadcastProcessQueue(@NonNull BroadcastConstants constants,
@NonNull String processName, int uid) {
this.constants = Objects.requireNonNull(constants);
@@ -327,7 +330,7 @@
}
public boolean isEmpty() {
- return (mActive != null) && mPending.isEmpty();
+ return mPending.isEmpty();
}
public boolean isActive() {
@@ -385,6 +388,12 @@
} else {
mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
}
+
+ // If we have too many broadcasts pending, bypass any delays that
+ // might have been applied above to aid draining
+ if (mPending.size() >= constants.MAX_PENDING_BROADCASTS) {
+ mRunnableAt = runnableAt;
+ }
} else {
mRunnableAt = Long.MAX_VALUE;
}
@@ -452,13 +461,19 @@
@Override
public String toString() {
- return "BroadcastProcessQueue{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + processName + "/" + UserHandle.formatUid(uid) + "}";
+ if (mCachedToString == null) {
+ mCachedToString = "BroadcastProcessQueue{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + processName + "/" + UserHandle.formatUid(uid) + "}";
+ }
+ return mCachedToString;
}
public String toShortString() {
- return processName + "/" + UserHandle.formatUid(uid);
+ if (mCachedToShortString == null) {
+ mCachedToShortString = processName + "/" + UserHandle.formatUid(uid);
+ }
+ return mCachedToShortString;
}
public void dumpLocked(@NonNull IndentingPrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 972a1ce..b46a2b2 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -114,6 +114,9 @@
/**
* Signal from OS internals that the given process has just been actively
* attached, and is ready to begin receiving broadcasts.
+ *
+ * @return if the queue performed an action on the given process, such as
+ * dispatching a pending broadcast
*/
@GuardedBy("mService")
public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app);
@@ -123,7 +126,7 @@
* an attempted start and attachment.
*/
@GuardedBy("mService")
- public abstract boolean onApplicationTimeoutLocked(@NonNull ProcessRecord app);
+ public abstract void onApplicationTimeoutLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process, which had already been
@@ -131,14 +134,14 @@
* not responding.
*/
@GuardedBy("mService")
- public abstract boolean onApplicationProblemLocked(@NonNull ProcessRecord app);
+ public abstract void onApplicationProblemLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process has been killed, and is
* no longer actively running.
*/
@GuardedBy("mService")
- public abstract boolean onApplicationCleanupLocked(@NonNull ProcessRecord app);
+ public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given package (or some subset of that
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 28bd9c3..1671185 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -426,16 +426,16 @@
}
}
- public boolean onApplicationTimeoutLocked(ProcessRecord app) {
- return skipCurrentOrPendingReceiverLocked(app);
+ public void onApplicationTimeoutLocked(ProcessRecord app) {
+ skipCurrentOrPendingReceiverLocked(app);
}
- public boolean onApplicationProblemLocked(ProcessRecord app) {
- return skipCurrentOrPendingReceiverLocked(app);
+ public void onApplicationProblemLocked(ProcessRecord app) {
+ skipCurrentOrPendingReceiverLocked(app);
}
- public boolean onApplicationCleanupLocked(ProcessRecord app) {
- return skipCurrentOrPendingReceiverLocked(app);
+ public void onApplicationCleanupLocked(ProcessRecord app) {
+ skipCurrentOrPendingReceiverLocked(app);
}
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
@@ -732,9 +732,10 @@
} catch (RemoteException ex) {
// Failed to call into the process. It's either dying or wedged. Kill it gently.
synchronized (mService) {
- Slog.w(TAG, "Can't deliver broadcast to " + app.processName
- + " (pid " + app.getPid() + "). Crashing it.");
- app.scheduleCrashLocked("can't deliver broadcast",
+ final String msg = "Failed to schedule " + intent + " to " + receiver
+ + " via " + app + ": " + ex;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg,
CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
}
throw ex;
@@ -814,7 +815,11 @@
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
- if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
+ final boolean isInFullBackup = (filter.receiverList.app != null)
+ && filter.receiverList.app.isInFullBackup();
+ final boolean isKilled = (filter.receiverList.app != null)
+ && filter.receiverList.app.isKilled();
+ if (isInFullBackup || isKilled) {
// Skip delivery if full backup in progress
// If it's an ordered broadcast, we need to continue to the next receiver.
if (ordered) {
@@ -1379,8 +1384,11 @@
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
- Slog.w(TAG, "Exception when sending broadcast to "
- + r.curComponent, e);
+ final String msg = "Failed to schedule " + r.intent + " to " + info
+ + " via " + app + ": " + e;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg,
+ CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Failed sending broadcast to "
+ r.curComponent + " with " + r.intent, e);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index a36a9f6..7c236ff 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -290,7 +290,7 @@
}
// If app isn't running, and there's nothing in the queue, clean up
- if (queue.isEmpty() && !queue.isProcessWarm()) {
+ if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
removeProcessQueue(queue.processName, queue.uid);
}
}
@@ -420,18 +420,17 @@
}
@Override
- public boolean onApplicationTimeoutLocked(@NonNull ProcessRecord app) {
- return onApplicationCleanupLocked(app);
+ public void onApplicationTimeoutLocked(@NonNull ProcessRecord app) {
+ onApplicationCleanupLocked(app);
}
@Override
- public boolean onApplicationProblemLocked(@NonNull ProcessRecord app) {
- return onApplicationCleanupLocked(app);
+ public void onApplicationProblemLocked(@NonNull ProcessRecord app) {
+ onApplicationCleanupLocked(app);
}
@Override
- public boolean onApplicationCleanupLocked(@NonNull ProcessRecord app) {
- boolean didSomething = false;
+ public void onApplicationCleanupLocked(@NonNull ProcessRecord app) {
if ((mRunningColdStart != null) && (mRunningColdStart.app == app)) {
// We've been waiting for this app to cold start, and it had
// trouble; clear the slot and fail delivery below
@@ -439,7 +438,6 @@
// We might be willing to kick off another cold start
enqueueUpdateRunningList();
- didSomething = true;
}
final BroadcastProcessQueue queue = getProcessQueue(app);
@@ -449,16 +447,19 @@
// If queue was running a broadcast, fail it
if (queue.isActive()) {
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
- didSomething = true;
}
+ // Skip any pending registered receivers, since the old process
+ // would never be around to receive them
+ queue.removeMatchingBroadcasts((r, i) -> {
+ return (r.receivers.get(i) instanceof BroadcastFilter);
+ }, mBroadcastConsumerSkip);
+
// If queue has nothing else pending, consider cleaning it
if (queue.isEmpty()) {
updateRunnableList(queue);
}
}
-
- return didSomething;
}
@Override
@@ -515,6 +516,13 @@
final int index = queue.getActiveIndex();
final Object receiver = r.receivers.get(index);
+ // Ignore registered receivers from a previous PID
+ if (receiver instanceof BroadcastFilter) {
+ mRunningColdStart = null;
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+ return;
+ }
+
final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();
@@ -536,6 +544,7 @@
} else {
mRunningColdStart = null;
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
+ return;
}
}
@@ -563,7 +572,7 @@
return;
}
- // Consider additional cases where we'd want fo finish immediately
+ // Consider additional cases where we'd want to finish immediately
if (app.isInFullBackup()) {
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
return;
@@ -578,7 +587,14 @@
return;
}
- if (!r.timeoutExempt) {
+ // Ignore registered receivers from a previous PID
+ if ((receiver instanceof BroadcastFilter)
+ && ((BroadcastFilter) receiver).receiverList.pid != app.getPid()) {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+ return;
+ }
+
+ if (mService.mProcessesReady && !r.timeoutExempt) {
final long timeout = r.isForeground() ? mFgConstants.TIMEOUT : mBgConstants.TIMEOUT;
mLocalHandler.sendMessageDelayed(
Message.obtain(mLocalHandler, MSG_DELIVERY_TIMEOUT, queue), timeout);
@@ -604,7 +620,7 @@
}
if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
- setDeliveryState(queue, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);
+ setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);
final IApplicationThread thread = app.getThread();
if (thread != null) {
@@ -628,8 +644,12 @@
app.mState.getReportedProcState());
}
} catch (RemoteException e) {
+ final String msg = "Failed to schedule " + r + " to " + receiver
+ + " via " + app + ": " + e;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg, CannotDeliverBroadcastException.TYPE_ID, null);
+ app.setKilled(true);
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
- app.scheduleCrashLocked(TAG, CannotDeliverBroadcastException.TYPE_ID, null);
}
} else {
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
@@ -652,7 +672,9 @@
r.resultCode, r.resultData, r.resultExtras, false, r.initialSticky,
r.userId, app.mState.getReportedProcState());
} catch (RemoteException e) {
- app.scheduleCrashLocked(TAG, CannotDeliverBroadcastException.TYPE_ID, null);
+ final String msg = "Failed to schedule result of " + r + " via " + app + ": " + e;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg, CannotDeliverBroadcastException.TYPE_ID, null);
}
}
}
@@ -674,7 +696,8 @@
// receivers as skipped
if (r.ordered && r.resultAbort) {
for (int i = r.finishedCount + 1; i < r.receivers.size(); i++) {
- setDeliveryState(null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
+ setDeliveryState(null, null, r, i, r.receivers.get(i),
+ BroadcastRecord.DELIVERY_SKIPPED);
}
}
@@ -690,9 +713,10 @@
final int index = queue.getActiveIndex();
final Object receiver = r.receivers.get(index);
- setDeliveryState(queue, r, index, receiver, deliveryState);
+ setDeliveryState(queue, app, r, index, receiver, deliveryState);
if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
+ r.anrCount++;
if (app != null && !app.isDebugging()) {
mService.appNotResponding(queue.app, TimeoutRecord
.forBroadcastReceiver("Broadcast of " + r.toShortString()));
@@ -704,7 +728,7 @@
// Even if we have more broadcasts, if we've made reasonable progress
// and someone else is waiting, retire ourselves to avoid starvation
final boolean shouldRetire = (mRunnableHead != null)
- && (queue.getActiveCountSinceIdle() > mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);
+ && (queue.getActiveCountSinceIdle() >= mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);
if (queue.isRunnable() && queue.isProcessWarm() && !shouldRetire) {
// We're on a roll; move onto the next broadcast for this process
@@ -733,17 +757,22 @@
* bookkeeping related to ordered broadcasts.
*/
private void setDeliveryState(@Nullable BroadcastProcessQueue queue,
- @NonNull BroadcastRecord r, int index, @NonNull Object receiver,
- @DeliveryState int newDeliveryState) {
+ @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
+ @NonNull Object receiver, @DeliveryState int newDeliveryState) {
final int oldDeliveryState = getDeliveryState(r, index);
if (newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
- Slog.w(TAG, "Delivery state of " + r + " to " + receiver + " changed from "
+ Slog.w(TAG, "Delivery state of " + r + " to " + receiver
+ + " via " + app + " changed from "
+ deliveryStateToString(oldDeliveryState) + " to "
+ deliveryStateToString(newDeliveryState));
}
- r.setDeliveryState(index, newDeliveryState);
+ // Only apply state when we haven't already reached a terminal state;
+ // this is how we ignore racing timeout messages
+ if (!isDeliveryStateTerminal(oldDeliveryState)) {
+ r.setDeliveryState(index, newDeliveryState);
+ }
// Emit any relevant tracing results when we're changing the delivery
// state as part of running from a queue
@@ -837,7 +866,7 @@
* of it matching a predicate.
*/
private final BroadcastConsumer mBroadcastConsumerSkip = (r, i) -> {
- setDeliveryState(null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
+ setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
};
private boolean skipMatchingBroadcasts(
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index ae7f2a5..33f74f3 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -128,6 +128,9 @@
@Nullable
final BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver;
+ String cachedToString;
+ String cachedToShortString;
+
static final int IDLE = 0;
static final int APP_RECEIVE = 1;
static final int CALL_IN_RECEIVE = 2;
@@ -700,21 +703,27 @@
@Override
public String toString() {
- String label = intent.getAction();
- if (label == null) {
- label = intent.toString();
+ if (cachedToString == null) {
+ String label = intent.getAction();
+ if (label == null) {
+ label = intent.toString();
+ }
+ cachedToString = "BroadcastRecord{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " u" + userId + " " + label + "}";
}
- return "BroadcastRecord{"
- + Integer.toHexString(System.identityHashCode(this))
- + " u" + userId + " " + label + "}";
+ return cachedToString;
}
public String toShortString() {
- String label = intent.getAction();
- if (label == null) {
- label = intent.toString();
+ if (cachedToShortString == null) {
+ String label = intent.getAction();
+ if (label == null) {
+ label = intent.toString();
+ }
+ cachedToShortString = label + "/u" + userId;
}
- return label + "/u" + userId;
+ return cachedToShortString;
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 7369e8f..4c15308 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.app.ActivityManager.processStateAmToProto;
import android.annotation.IntDef;
import android.app.IApplicationThread;
@@ -318,12 +317,6 @@
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
pkgList.getPackageListLocked());
- pkgList.forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- processStateAmToProto(ProcessStats.STATE_NOTHING),
- holder.appVersion)
- );
}
origBase.makeInactive();
}
@@ -362,12 +355,6 @@
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
pkgList.getPackageListLocked());
- pkgList.forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- processStateAmToProto(ProcessStats.STATE_NOTHING),
- holder.appVersion)
- );
}
origBase.makeInactive();
setBaseProcessTracker(null);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 07b6fcd..482e6a7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -54,7 +54,6 @@
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.Zygote;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
@@ -1212,12 +1211,6 @@
long now = SystemClock.uptimeMillis();
baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), now, mPkgList.getPackageListLocked());
- mPkgList.forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgName,
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- holder.appVersion)
- );
if (numOfPkgs != 1) {
mPkgList.forEachPackageProcessStats(holder -> {
if (holder.state != null && holder.state != baseProcessTracker) {
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index ef13778..d2ef479 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -35,7 +35,6 @@
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.PlatformCompatCache.CachedCompatChangeId;
import java.io.PrintWriter;
@@ -599,12 +598,6 @@
@GuardedBy({"mService", "mProcLock"})
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
- mApp.getPkgList().forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- ActivityManager.processStateAmToProto(mRepProcState),
- holder.appVersion)
- );
mApp.getWindowProcessController().setReportedProcState(repProcState);
}
@@ -620,12 +613,6 @@
mRepProcState = newState;
setCurProcState(newState);
setCurRawProcState(newState);
- mApp.getPkgList().forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- ActivityManager.processStateAmToProto(mRepProcState),
- holder.appVersion)
- );
}
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 403ad9b..bc650ad 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4817,7 +4817,7 @@
pw.println(" Only output the watcher sections.");
pw.println(" --history");
pw.println(" Only output history.");
- pw.println(" --uid-state-change-logs");
+ pw.println(" --uid-state-changes");
pw.println(" Include logs about uid state changes.");
}
@@ -5039,7 +5039,7 @@
} else if (arg.length() > 0 && arg.charAt(0) == '-') {
pw.println("Unknown option: " + arg);
return;
- } else if ("--uid-state-change-logs".equals(arg)) {
+ } else if ("--uid-state-changes".equals(arg)) {
dumpUidStateChangeLogs = true;
} else {
pw.println("Unknown command: " + arg);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2cde526..e851f03 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -80,7 +80,6 @@
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.WifiDisplayStatus;
import android.hardware.graphics.common.DisplayDecorationSupport;
-import android.hardware.input.InputManagerInternal;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.net.Uri;
@@ -134,6 +133,7 @@
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.utils.SensorUtils;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.SurfaceAnimationThread;
import com.android.server.wm.WindowManagerInternal;
diff --git a/services/core/java/com/android/server/display/TEST_MAPPING b/services/core/java/com/android/server/display/TEST_MAPPING
index 66ec5c4..57c2e01 100644
--- a/services/core/java/com/android/server/display/TEST_MAPPING
+++ b/services/core/java/com/android/server/display/TEST_MAPPING
@@ -7,6 +7,15 @@
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.display"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
}
]
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index fee1f5c..e1b18f2 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -38,7 +38,6 @@
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -66,6 +65,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e03a46c..3ee3503 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -266,10 +266,6 @@
// Make sure HdmiCecConfig is instantiated and the XMLs are read.
private HdmiCecConfig mHdmiCecConfig;
- // Last return value of getPhysicalAddress(). Only updated on calls of getPhysicalAddress().
- // Does not represent the current physical address at all times. Not to be used as a cache.
- private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
-
/**
* Interface to report send result.
*/
@@ -2082,15 +2078,9 @@
@Override
public int getPhysicalAddress() {
initBinderCall();
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- synchronized (mLock) {
- mPhysicalAddress = mHdmiCecNetwork.getPhysicalAddress();
- }
- }
- });
- return mPhysicalAddress;
+ synchronized (mLock) {
+ return mHdmiCecNetwork.getPhysicalAddress();
+ }
}
@Override
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
similarity index 98%
rename from core/java/android/hardware/input/InputManagerInternal.java
rename to services/core/java/com/android/server/input/InputManagerInternal.java
index fc6bc55..36099b0 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.input;
+package com.android.server.input;
import android.annotation.NonNull;
import android.graphics.PointF;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 05c4f77..71feb95 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -55,8 +55,6 @@
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
-import android.hardware.input.InputManagerInternal.LidSwitchCallback;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
@@ -123,6 +121,7 @@
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
+import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.policy.WindowManagerPolicy;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index f89b6ae..a4830be 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -21,7 +21,6 @@
import android.annotation.AnyThread;
import android.annotation.Nullable;
import android.annotation.UiThread;
-import android.hardware.input.InputManagerInternal;
import android.os.IBinder;
import android.os.Looper;
import android.util.Slog;
@@ -35,6 +34,7 @@
import android.view.SurfaceControl;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ed17b9ca..4571546 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -92,7 +92,6 @@
import android.graphics.Matrix;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManagerInternal;
import android.net.Uri;
@@ -188,6 +187,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 82b5c11..cc8761d 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -621,10 +621,9 @@
}
}
- //TODO(b/136703681): Review this is handling multi-user properly.
- void switchUser() {
+ // TODO(b/136703681): Review this is handling multi-user properly.
+ void switchUser(int userId) {
synchronized (mLock) {
- int userId = ActivityManager.getCurrentUser();
if (mCurrentUserId != userId) {
final int oldUserId = mCurrentUserId;
mCurrentUserId = userId; // do this first
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 4f0da795..d7d0b42 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -17,7 +17,9 @@
package com.android.server.media;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
+import android.app.UserSwitchObserver;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
@@ -146,18 +148,27 @@
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
}
- public void systemRunning() {
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {
- switchUser();
- }
- }
- }, filter);
-
- switchUser();
+ /**
+ * Initializes the MediaRouter service.
+ *
+ * @throws RemoteException If an error occurs while registering the {@link UserSwitchObserver}.
+ */
+ @RequiresPermission(
+ anyOf = {
+ "android.permission.INTERACT_ACROSS_USERS",
+ "android.permission.INTERACT_ACROSS_USERS_FULL"
+ })
+ public void systemRunning() throws RemoteException {
+ ActivityManager.getService()
+ .registerUserSwitchObserver(
+ new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ switchUser(newUserId);
+ }
+ },
+ TAG);
+ switchUser(ActivityManager.getCurrentUser());
}
@Override
@@ -634,9 +645,8 @@
}
}
- void switchUser() {
+ void switchUser(int userId) {
synchronized (mLock) {
- int userId = ActivityManager.getCurrentUser();
if (mCurrentUserId != userId) {
final int oldUserId = mCurrentUserId;
mCurrentUserId = userId; // do this first
@@ -653,7 +663,7 @@
}
}
}
- mService2.switchUser();
+ mService2.switchUser(userId);
}
void clientDied(ClientRecord clientRecord) {
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 2b8a196..cd074c0 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -385,6 +385,11 @@
} else if (snapshot.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
return false;
}
+ var pkg = snapshot.getPackage(options.getPackageName());
+ if (pkg != null && pkg.isApex()) {
+ // skip APEX
+ return true;
+ }
if (options.isDexoptOnlySecondaryDex()) {
return mPm.getDexManager().dexoptSecondaryDex(options);
@@ -427,6 +432,10 @@
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
+ if (p.isApex()) {
+ // APEX needs no dexopt
+ return PackageDexOptimizer.DEX_OPT_SKIPPED;
+ }
mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
mPm.mCompilerStats.maybeWriteAsync();
}
@@ -498,6 +507,9 @@
if (packageState == null || pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
+ if (pkg.isApex()) {
+ throw new IllegalArgumentException("Can't dexopt APEX package: " + packageName);
+ }
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
diff --git a/services/core/java/com/android/server/pm/InstallSource.java b/services/core/java/com/android/server/pm/InstallSource.java
index 404285c..e5f7f71 100644
--- a/services/core/java/com/android/server/pm/InstallSource.java
+++ b/services/core/java/com/android/server/pm/InstallSource.java
@@ -131,7 +131,8 @@
@Nullable PackageSignatures initiatingPackageSignatures) {
if (initiatingPackageName == null && originatingPackageName == null
&& installerPackageName == null && initiatingPackageSignatures == null
- && !isInitiatingPackageUninstalled) {
+ && !isInitiatingPackageUninstalled
+ && packageSource == PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED) {
return isOrphaned ? EMPTY_ORPHANED : EMPTY;
}
return new InstallSource(initiatingPackageName, originatingPackageName,
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 3fb4066..178d0ea 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -189,6 +189,11 @@
return false;
}
+ // We do not dexopt APEX packages.
+ if (pkg.isApex()) {
+ return false;
+ }
+
// We do not dexopt unused packages.
// It's possible for this to be called before app hibernation service is ready due to
// an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 9b7d19a..f8fcaff 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -26,7 +26,6 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
import android.os.Environment;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -39,6 +38,7 @@
import com.android.server.LocalServices;
import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
import com.android.server.policy.devicestate.config.Conditions;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
import com.android.server.policy.devicestate.config.Flags;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 02d7074..07f5bcf 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -121,7 +121,6 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioSystem;
@@ -204,6 +203,7 @@
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 9336be5..69fb22c 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -62,6 +61,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d8d75ed..9ed77fc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1354,10 +1354,6 @@
return true;
}
- void setAppTimeTracker(AppTimeTracker att) {
- appTimeTracker = att;
- }
-
/** Update the saved state of an activity. */
void setSavedState(@Nullable Bundle savedState) {
mIcicle = savedState;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 5bddae6..b473700 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -139,7 +139,6 @@
import com.android.internal.content.ReferrerIntent;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
@@ -1578,17 +1577,12 @@
if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) {
removePinnedRootTaskInSurfaceTransaction(rootTask);
} else {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityTaskSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
- rootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
+ rootTask.forAllLeafTasks(task -> {
+ removeTask(task, true /* killProcess */, REMOVE_FROM_RECENTS, "remove-root-task");
+ }, true /* traverseTopToBottom */);
}
}
- private void processRemoveTask(Task task) {
- removeTask(task, true /* killProcess */, REMOVE_FROM_RECENTS, "remove-root-task");
- }
-
/**
* Removes the root task associated with the given {@param task}. If the {@param task} is the
* pinned task, then its child tasks are not explicitly removed when the root task is
@@ -2268,23 +2262,17 @@
}
void scheduleUpdateMultiWindowMode(Task task) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityTaskSupervisor::addToMultiWindowModeChangedList, this,
- PooledLambda.__(ActivityRecord.class));
- task.forAllActivities(c);
- c.recycle();
+ task.forAllActivities(r -> {
+ if (r.attachedToProcess()) {
+ mMultiWindowModeChangedActivities.add(r);
+ }
+ });
if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
}
}
- private void addToMultiWindowModeChangedList(ActivityRecord r) {
- if (r.attachedToProcess()) {
- mMultiWindowModeChangedActivities.add(r);
- }
- }
-
void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Task prevRootTask) {
final Task rootTask = task.getRootTask();
if ((prevRootTask == null || (prevRootTask != rootTask
@@ -2296,11 +2284,14 @@
}
void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetRootTaskBounds) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityTaskSupervisor::addToPipModeChangedList, this,
- PooledLambda.__(ActivityRecord.class));
- task.forAllActivities(c);
- c.recycle();
+ task.forAllActivities(r -> {
+ if (!r.attachedToProcess()) return;
+ mPipModeChangedActivities.add(r);
+ // If we are scheduling pip change, then remove this activity from multi-window
+ // change list as the processing of pip change will make sure multi-window changed
+ // message is processed in the right order relative to pip changed.
+ mMultiWindowModeChangedActivities.remove(r);
+ });
mPipModeChangedTargetRootTaskBounds = targetRootTaskBounds;
@@ -2309,16 +2300,6 @@
}
}
- private void addToPipModeChangedList(ActivityRecord r) {
- if (!r.attachedToProcess()) return;
-
- mPipModeChangedActivities.add(r);
- // If we are scheduling pip change, then remove this activity from multi-window
- // change list as the processing of pip change will make sure multi-window changed
- // message is processed in the right order relative to pip changed.
- mMultiWindowModeChangedActivities.remove(r);
- }
-
void wakeUp(String reason) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,
"android.server.am:TURN_ON:" + reason);
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index acbf1a4..6e23ed9 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -391,8 +391,7 @@
* </p>
*/
private void handleStartRecordingFailed() {
- final boolean shouldExitTaskRecording = mContentRecordingSession != null
- && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+ final boolean shouldExitTaskRecording = isRecordingContentTask();
clearContentRecordingSession();
if (shouldExitTaskRecording) {
// Clean up the cached session first to ensure recording doesn't re-start, since
@@ -478,9 +477,10 @@
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Recorded task is removed, so stop recording on display %d",
mDisplayContent.getDisplayId());
- Task recordedTask = mRecordedWindowContainer.asTask();
- if (recordedTask == null
- || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
+
+ Task recordedTask = mRecordedWindowContainer != null
+ ? mRecordedWindowContainer.asTask() : null;
+ if (recordedTask == null || !isRecordingContentTask()) {
return;
}
recordedTask.unregisterWindowContainerListener(this);
@@ -504,4 +504,9 @@
@VisibleForTesting interface MediaProjectionManagerWrapper {
void stopActiveProjection();
}
+
+ private boolean isRecordingContentTask() {
+ return mContentRecordingSession != null
+ && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c6fe017..669cd5d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -241,7 +241,6 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.function.TriConsumer;
-import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -3075,18 +3074,13 @@
*/
boolean pointWithinAppWindow(int x, int y) {
final int[] targetWindowType = {-1};
- final PooledConsumer fn = PooledLambda.obtainConsumer((w, nonArg) -> {
- if (targetWindowType[0] != -1) {
- return;
- }
-
+ forAllWindows(w -> {
if (w.isOnScreen() && w.isVisible() && w.getFrame().contains(x, y)) {
targetWindowType[0] = w.mAttrs.type;
- return;
+ return true;
}
- }, PooledLambda.__(WindowState.class), mTmpRect);
- forAllWindows(fn, true /* traverseTopToBottom */);
- fn.recycle();
+ return false;
+ }, true /* traverseTopToBottom */);
return FIRST_APPLICATION_WINDOW <= targetWindowType[0]
&& targetWindowType[0] <= LAST_APPLICATION_WINDOW;
}
@@ -3112,11 +3106,7 @@
mTmpRect.setEmpty();
mTmpRect2.setEmpty();
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayContent::processTaskForTouchExcludeRegion, this,
- PooledLambda.__(Task.class), focusedTask, delta);
- forAllTasks(c);
- c.recycle();
+ forAllTasks(t -> { processTaskForTouchExcludeRegion(t, focusedTask, delta); });
// If we removed the focused task above, add it back and only leave its
// outside touch area in the exclusion. TapDetector is not interested in
@@ -6187,17 +6177,10 @@
/** Update and get all UIDs that are present on the display and have access to it. */
IntArray getPresentUIDs() {
mDisplayAccessUIDs.clear();
- final PooledConsumer c = PooledLambda.obtainConsumer(DisplayContent::addActivityUid,
- PooledLambda.__(ActivityRecord.class), mDisplayAccessUIDs);
- mDisplayContent.forAllActivities(c);
- c.recycle();
+ mDisplayContent.forAllActivities(r -> { mDisplayAccessUIDs.add(r.getUid()); });
return mDisplayAccessUIDs;
}
- private static void addActivityUid(ActivityRecord r, IntArray uids) {
- uids.add(r.getUid());
- }
-
@VisibleForTesting
boolean shouldDestroyContentOnRemove() {
return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e780606..ac61cb9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2167,10 +2167,7 @@
* If the decor insets changes, the display configuration may be affected. The caller should
* call {@link DisplayContent#sendNewConfiguration()} if this method returns {@code true}.
*/
- boolean updateDecorInsetsInfoIfNeeded(WindowState win) {
- if (!win.providesNonDecorInsets()) {
- return false;
- }
+ boolean updateDecorInsetsInfo() {
final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames;
final int rotation = displayFrames.mRotation;
final int dw = displayFrames.mWidth;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 9e01f10..bffab7a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -62,8 +62,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -403,11 +401,11 @@
final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
.getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
if (targetRootTask != null) {
- final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
- { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
- visibleTasks);
- targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
+ targetRootTask.forAllLeafTasks(t -> {
+ if (!visibleTasks.contains(t)) {
+ visibleTasks.add(t);
+ }
+ }, true /* traverseTopToBottom */);
}
final int taskCount = visibleTasks.size();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ba834de..b2ec3f3 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3102,9 +3102,8 @@
}
void finishVoiceTask(IVoiceInteractionSession session) {
- forAllRootTasks(rootTask -> {
- rootTask.finishVoiceTask(session);
- });
+ final IBinder binder = session.asBinder();
+ forAllLeafTasks(t -> t.finishIfVoiceTask(binder), true /* traverseTopToBottom */);
}
/**
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 120fec0..a753b55 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -23,18 +23,16 @@
import android.os.UserHandle;
import android.util.ArraySet;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
-
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
+import java.util.function.Consumer;
/**
* Class for resolving the set of running tasks in the system.
*/
-class RunningTasks {
+class RunningTasks implements Consumer<Task> {
static final int FLAG_FILTER_ONLY_VISIBLE_RECENTS = 1;
static final int FLAG_ALLOWED = 1 << 1;
@@ -61,7 +59,7 @@
private boolean mKeepIntentExtra;
void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
- WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
return;
@@ -79,10 +77,7 @@
mRecentTasks = recentTasks;
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
- final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
- PooledLambda.__(Task.class));
- root.forAllLeafTasks(c, false);
- c.recycle();
+ root.forAllLeafTasks(this, false /* traverseTopToBottom */);
// Take the first {@param maxNum} tasks and create running task infos for them
final Iterator<Task> iter = mTmpSortedSet.iterator();
@@ -97,7 +92,8 @@
}
}
- private void processTask(Task task) {
+ @Override
+ public void accept(Task task) {
if (task.getTopNonFinishingActivity() == null) {
// Skip if there are no activities in the task
return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1508870..63c6c35 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -196,7 +196,6 @@
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.XmlUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.Watchdog;
@@ -1188,11 +1187,7 @@
if (oldParent != null) {
final Task oldParentTask = oldParent.asTask();
if (oldParentTask != null) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- Task::cleanUpActivityReferences, oldParentTask,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(c);
- c.recycle();
+ forAllActivities(oldParentTask::cleanUpActivityReferences);
}
if (oldParent.inPinnedWindowingMode()
@@ -2371,10 +2366,7 @@
int getDescendantTaskCount() {
final int[] currentCount = {0};
- final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
- PooledLambda.__(Task.class), currentCount);
- forAllLeafTasks(c, false /* traverseTopToBottom */);
- c.recycle();
+ forAllLeafTasks(t -> currentCount[0]++, false /* traverseTopToBottom */);
return currentCount[0];
}
@@ -2783,10 +2775,7 @@
&& displayContent.mDividerControllerLocked.isResizing();
if (inFreeformWindowingMode()) {
boolean[] foundTop = { false };
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::getMaxVisibleBounds,
- PooledLambda.__(ActivityRecord.class), out, foundTop);
- forAllActivities(c);
- c.recycle();
+ forAllActivities(a -> { getMaxVisibleBounds(a, out, foundTop); });
if (foundTop[0]) {
return;
}
@@ -4349,14 +4338,6 @@
}
}
-
- void setActivityWindowingMode(int windowingMode) {
- PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
- PooledLambda.__(ActivityRecord.class), windowingMode);
- forAllActivities(c);
- c.recycle();
- }
-
/**
* Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
* @return Whether the force hidden state changed
@@ -5209,26 +5190,19 @@
return finishedTask;
}
- void finishVoiceTask(IVoiceInteractionSession session) {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::finishIfVoiceTask,
- PooledLambda.__(Task.class), session.asBinder());
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- private static void finishIfVoiceTask(Task tr, IBinder binder) {
- if (tr.voiceSession != null && tr.voiceSession.asBinder() == binder) {
- tr.forAllActivities((r) -> {
+ void finishIfVoiceTask(IBinder binder) {
+ if (voiceSession != null && voiceSession.asBinder() == binder) {
+ forAllActivities((r) -> {
if (r.finishing) return;
r.finishIfPossible("finish-voice", false /* oomAdj */);
- tr.mAtmService.updateOomAdj();
+ mAtmService.updateOomAdj();
});
} else {
// Check if any of the activities are using voice
final PooledPredicate f = PooledLambda.obtainPredicate(
Task::finishIfVoiceActivity, PooledLambda.__(ActivityRecord.class),
binder);
- tr.forAllActivities(f);
+ forAllActivities(f);
f.recycle();
}
}
@@ -5447,10 +5421,7 @@
if (timeTracker != null) {
// The caller wants a time tracker associated with this task.
- final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,
- PooledLambda.__(ActivityRecord.class), timeTracker);
- tr.forAllActivities(c);
- c.recycle();
+ tr.forAllActivities(a -> { a.appTimeTracker = timeTracker; });
}
try {
@@ -5621,11 +5592,11 @@
try {
// TODO: Why not just set this on the root task directly vs. on each tasks?
// Update override configurations of all tasks in the root task.
- final PooledConsumer c = PooledLambda.obtainConsumer(
- Task::processTaskResizeBounds, PooledLambda.__(Task.class),
- displayedBounds);
- forAllTasks(c, true /* traverseTopToBottom */);
- c.recycle();
+ forAllTasks(task -> {
+ if (task.isResizeable()) {
+ task.setBounds(displayedBounds);
+ }
+ }, true /* traverseTopToBottom */);
if (!deferResume) {
ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
@@ -5636,12 +5607,6 @@
}
}
- private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
- if (!task.isResizeable()) return;
-
- task.setBounds(displayedBounds);
- }
-
boolean willActivityBeVisible(IBinder token) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
@@ -5727,22 +5692,15 @@
// All activities that came from the package must be
// restarted as if there was a config change.
- PooledConsumer c = PooledLambda.obtainConsumer(Task::restartPackage,
- PooledLambda.__(ActivityRecord.class), starting, packageName);
- forAllActivities(c);
- c.recycle();
-
- return starting;
- }
-
- private static void restartPackage(
- ActivityRecord r, ActivityRecord starting, String packageName) {
- if (r.info.packageName.equals(packageName)) {
+ forAllActivities(r -> {
+ if (!r.info.packageName.equals(packageName)) return;
r.forceNewConfig = true;
if (starting != null && r == starting && r.mVisibleRequested) {
r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
}
- }
+ });
+
+ return starting;
}
Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 2cfc563..4f1a561 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1719,7 +1719,7 @@
ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
prev = prev.completeFinishing(false /* updateVisibility */,
"completePausedLocked");
- } else if (prev.hasProcess()) {
+ } else if (prev.attachedToProcess()) {
ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
+ "wasStopping=%b visibleRequested=%b", prev, wasStopping,
prev.mVisibleRequested);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 69d86b6..9763df6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -477,7 +477,10 @@
}
mLocalInsetsSourceProviders.remove(insetsTypes[i]);
}
- mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+ // Update insets if this window is attached.
+ if (mDisplayContent != null) {
+ mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e23e206..29ab562 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -309,8 +309,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
@@ -475,10 +473,7 @@
public void onVrStateChanged(boolean enabled) {
synchronized (mGlobalLock) {
mVrModeEnabled = enabled;
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(p -> p.onVrStateChangedLw(enabled));
}
}
};
@@ -900,11 +895,8 @@
}
mPointerLocationEnabled = enablePointerLocation;
synchronized (mGlobalLock) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::setPointerLocationEnabled, PooledLambda.__(),
- mPointerLocationEnabled);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(
+ p -> p.setPointerLocationEnabled(mPointerLocationEnabled));
}
}
@@ -1834,8 +1826,12 @@
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
- if ((win.isVisibleRequestedOrAdding() && displayContent.updateOrientation())
- || displayPolicy.updateDecorInsetsInfoIfNeeded(win)) {
+ boolean needToSendNewConfiguration =
+ win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
+ if (win.providesNonDecorInsets()) {
+ needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
+ }
+ if (needToSendNewConfiguration) {
displayContent.sendNewConfiguration();
}
@@ -2304,8 +2300,8 @@
& WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
win.mLayoutNeeded = true;
}
- if (layoutChanged) {
- configChanged = displayPolicy.updateDecorInsetsInfoIfNeeded(win);
+ if (layoutChanged && win.providesNonDecorInsets()) {
+ configChanged = displayPolicy.updateDecorInsetsInfo();
}
if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
@@ -3135,10 +3131,7 @@
@Override
public void onPowerKeyDown(boolean isScreenOn) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(p -> p.onPowerKeyDown(isScreenOn));
}
@Override
@@ -8387,10 +8380,7 @@
void onLockTaskStateChanged(int lockTaskState) {
// TODO: pass in displayId to determine which display the lock task state changed
synchronized (mGlobalLock) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(p -> p.onLockTaskStateChangedLw(lockTaskState));
}
}
@@ -9282,4 +9272,46 @@
"Unexpected letterbox background type: " + letterboxBackgroundType);
}
}
+
+ @Override
+ public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener listener) {
+ Slog.d(TAG, "captureDisplay");
+ if (!checkCallingPermission(READ_FRAME_BUFFER, "captureDisplay()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+
+ ScreenCapture.captureLayers(getCaptureArgs(displayId, captureArgs), listener);
+ }
+
+ @VisibleForTesting
+ ScreenCapture.LayerCaptureArgs getCaptureArgs(int displayId,
+ @Nullable ScreenCapture.CaptureArgs captureArgs) {
+ final SurfaceControl displaySurfaceControl;
+ synchronized (mGlobalLock) {
+ DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to screenshot and invalid display: "
+ + displayId);
+ }
+
+ displaySurfaceControl = displayContent.getSurfaceControl();
+
+ if (captureArgs == null) {
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .build();
+ }
+
+ if (captureArgs.mSourceCrop.isEmpty()) {
+ displayContent.getBounds(mTmpRect);
+ mTmpRect.offsetTo(0, 0);
+ } else {
+ mTmpRect.set(captureArgs.mSourceCrop);
+ }
+ }
+
+ return new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl, captureArgs)
+ .setSourceCrop(mTmpRect)
+ .build();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 2e1477d..e2f833c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -91,8 +91,6 @@
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal;
@@ -482,7 +480,7 @@
}
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
- ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
+ final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
t.getChanges().entrySet().iterator();
while (entries.hasNext()) {
@@ -592,16 +590,10 @@
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
} else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
- final PooledConsumer f = PooledLambda.obtainConsumer(
- ActivityRecord::ensureActivityConfiguration,
- PooledLambda.__(ActivityRecord.class), 0,
- true /* preserveWindow */);
- try {
- for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
- haveConfigChanges.valueAt(i).forAllActivities(f);
- }
- } finally {
- f.recycle();
+ for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
+ haveConfigChanges.valueAt(i).forAllActivities(r -> {
+ r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);
+ });
}
}
@@ -716,7 +708,7 @@
final int childWindowingMode = c.getActivityWindowingMode();
if (childWindowingMode > -1) {
- tr.setActivityWindowingMode(childWindowingMode);
+ tr.forAllActivities(a -> { a.setWindowingMode(childWindowingMode); });
}
if (t != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7434ea0..68fabc5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2626,11 +2626,19 @@
}
}
+ // Check if window provides non decor insets before clearing its provided insets.
+ final boolean windowProvidesNonDecorInsets = providesNonDecorInsets();
+
removeImmediately();
// Removing a visible window may affect the display orientation so just update it if
// needed. Also recompute configuration if it provides screen decor insets.
- if ((wasVisible && displayContent.updateOrientation())
- || displayContent.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(this)) {
+ boolean needToSendNewConfiguration = wasVisible && displayContent.updateOrientation();
+ if (windowProvidesNonDecorInsets) {
+ needToSendNewConfiguration |=
+ displayContent.getDisplayPolicy().updateDecorInsetsInfo();
+ }
+
+ if (needToSendNewConfiguration) {
displayContent.sendNewConfiguration();
}
mWmService.updateFocusedWindowLocked(isFocused()
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 78b4ce2..3f380e7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -168,8 +168,9 @@
jmethodID constructor;
jfieldID lightTypeInput;
jfieldID lightTypePlayerId;
+ jfieldID lightTypeKeyboardBacklight;
jfieldID lightCapabilityBrightness;
- jfieldID lightCapabilityRgb;
+ jfieldID lightCapabilityColorRgb;
} gLightClassInfo;
static struct {
@@ -2011,25 +2012,28 @@
jint jTypeId =
env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
- jint jCapability = 0;
-
- if (lightInfo.type == InputDeviceLightType::MONO) {
- jCapability = env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityBrightness);
- } else if (lightInfo.type == InputDeviceLightType::RGB ||
- lightInfo.type == InputDeviceLightType::MULTI_COLOR) {
- jCapability =
- env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityBrightness) |
- env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityRgb);
+ if (lightInfo.type == InputDeviceLightType::INPUT) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
} else if (lightInfo.type == InputDeviceLightType::PLAYER_ID) {
jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
gLightClassInfo.lightTypePlayerId);
+ } else if (lightInfo.type == InputDeviceLightType::KEYBOARD_BACKLIGHT) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightTypeKeyboardBacklight);
} else {
ALOGW("Unknown light type %d", lightInfo.type);
continue;
}
+
+ jint jCapability = 0;
+ if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)) {
+ jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightCapabilityBrightness);
+ }
+ if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::RGB)) {
+ jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightCapabilityColorRgb);
+ }
ScopedLocalRef<jobject> lightObj(env,
env->NewObject(gLightClassInfo.clazz,
gLightClassInfo.constructor,
@@ -2596,10 +2600,12 @@
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT", "I");
gLightClassInfo.lightTypePlayerId =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_PLAYER_ID", "I");
+ gLightClassInfo.lightTypeKeyboardBacklight =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_BACKLIGHT", "I");
gLightClassInfo.lightCapabilityBrightness =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_BRIGHTNESS", "I");
- gLightClassInfo.lightCapabilityRgb =
- env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_RGB", "I");
+ gLightClassInfo.lightCapabilityColorRgb =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_COLOR_RGB", "I");
// ArrayList
FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList");
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 6196c49..9c9b363 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1287,8 +1287,8 @@
bp.set_allocated_dest_path(&target);
bp.set_allocated_source_subdir(&source);
const auto metadata = bp.SerializeAsString();
- bp.release_dest_path();
- bp.release_source_subdir();
+ static_cast<void>(bp.release_dest_path());
+ static_cast<void>(bp.release_source_subdir());
mdFileName = makeBindMdName();
metadataFullPath = path::join(ifs.root, constants().mount, mdFileName);
auto node = mIncFs->makeFile(ifs.control, metadataFullPath, 0444, idFromMetadata(metadata),
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
index 525a931..c8d153a 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -23,7 +23,6 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -35,6 +34,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.input.InputManagerInternal;
final class SelectionToolbarManagerServiceImpl extends
AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index cf5d113..89accf8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -42,6 +43,7 @@
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
+import android.app.RemoteServiceException.CannotDeliverBroadcastException;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -56,6 +58,7 @@
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -136,6 +139,12 @@
private BroadcastQueue mQueue;
/**
+ * When enabled {@link ActivityManagerService#startProcessLocked} will fail
+ * by returning {@code null}; otherwise it will spawn a new mock process.
+ */
+ private boolean mFailStartProcess;
+
+ /**
* Map from PID to registered registered runtime receivers.
*/
private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
@@ -185,10 +194,13 @@
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
+ if (mFailStartProcess) {
+ return null;
+ }
final String processName = invocation.getArgument(0);
final ApplicationInfo ai = invocation.getArgument(1);
- final ProcessRecord res = makeActiveProcessRecord(ai, processName, false,
- false, UnaryOperator.identity());
+ final ProcessRecord res = makeActiveProcessRecord(ai, processName,
+ ProcessBehavior.NORMAL, UnaryOperator.identity());
mHandlerThread.getThreadHandler().post(() -> {
synchronized (mAms) {
mQueue.onApplicationAttachedLocked(res);
@@ -197,6 +209,16 @@
return res;
}).when(mAms).startProcessLocked(any(), any(), anyBoolean(), anyInt(),
any(), anyInt(), anyBoolean(), anyBoolean());
+ doAnswer((invocation) -> {
+ final String processName = invocation.getArgument(0);
+ final int uid = invocation.getArgument(1);
+ for (ProcessRecord r : mActiveProcesses) {
+ if (Objects.equals(r.processName, processName) && r.uid == uid) {
+ return r;
+ }
+ }
+ return null;
+ }).when(mAms).getProcessRecordLocked(any(), anyInt());
doNothing().when(mAms).appNotResponding(any(), any());
final BroadcastConstants constants = new BroadcastConstants(
@@ -278,29 +300,51 @@
}
}
+ private enum ProcessBehavior {
+ /** Process broadcasts normally */
+ NORMAL,
+ /** Wedge and never confirm broadcast receipt */
+ WEDGE,
+ /** Process broadcast by requesting abort */
+ ABORT,
+ /** Appear to behave completely dead */
+ DEAD,
+ }
+
private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
final ApplicationInfo ai = makeApplicationInfo(packageName);
- return makeActiveProcessRecord(ai, ai.processName, false, false,
+ return makeActiveProcessRecord(ai, ai.processName, ProcessBehavior.NORMAL,
UnaryOperator.identity());
}
- private ProcessRecord makeWedgedActiveProcessRecord(String packageName) throws Exception {
+ private ProcessRecord makeActiveProcessRecord(String packageName,
+ ProcessBehavior behavior) throws Exception {
final ApplicationInfo ai = makeApplicationInfo(packageName);
- return makeActiveProcessRecord(ai, ai.processName, true, false,
+ return makeActiveProcessRecord(ai, ai.processName, behavior,
UnaryOperator.identity());
}
private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName,
- boolean wedged, boolean abort, UnaryOperator<Bundle> extrasOperator) throws Exception {
+ ProcessBehavior behavior, UnaryOperator<Bundle> extrasOperator) throws Exception {
+ final boolean wedge = (behavior == ProcessBehavior.WEDGE);
+ final boolean abort = (behavior == ProcessBehavior.ABORT);
+ final boolean dead = (behavior == ProcessBehavior.DEAD);
+
final ProcessRecord r = spy(new ProcessRecord(mAms, ai, processName, ai.uid));
r.setPid(mNextPid.getAndIncrement());
mActiveProcesses.add(r);
- final IApplicationThread thread = mock(IApplicationThread.class);
+ final IApplicationThread thread;
+ if (dead) {
+ thread = mock(IApplicationThread.class, (invocation) -> {
+ throw new DeadObjectException();
+ });
+ } else {
+ thread = mock(IApplicationThread.class);
+ }
final IBinder threadBinder = new Binder();
doReturn(threadBinder).when(thread).asBinder();
r.makeActive(thread, mAms.mProcessStats);
- doReturn(r).when(mAms).getProcessRecordLocked(eq(r.info.processName), eq(r.info.uid));
final IIntentReceiver receiver = mock(IIntentReceiver.class);
final IBinder receiverBinder = new Binder();
@@ -310,17 +354,28 @@
mRegisteredReceivers.put(r.getPid(), receiverList);
doAnswer((invocation) -> {
+ Log.v(TAG, "Intercepting killLocked() for "
+ + Arrays.toString(invocation.getArguments()));
+ mActiveProcesses.remove(r);
+ mRegisteredReceivers.remove(r.getPid());
+ return invocation.callRealMethod();
+ }).when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean());
+
+ // If we're entirely dead, rely on default behaviors above
+ if (dead) return r;
+
+ doAnswer((invocation) -> {
Log.v(TAG, "Intercepting scheduleReceiver() for "
+ Arrays.toString(invocation.getArguments()));
final Bundle extras = invocation.getArgument(5);
- if (!wedged) {
+ if (!wedge) {
assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
assertTrue(mQueue.getPreferredSchedulingGroupLocked(r)
!= ProcessList.SCHED_GROUP_UNDEFINED);
mHandlerThread.getThreadHandler().post(() -> {
synchronized (mAms) {
- mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
- null, extrasOperator.apply(extras), abort, false);
+ mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null,
+ extrasOperator.apply(extras), abort, false);
}
});
}
@@ -333,7 +388,7 @@
+ Arrays.toString(invocation.getArguments()));
final Bundle extras = invocation.getArgument(4);
final boolean ordered = invocation.getArgument(5);
- if (!wedged && ordered) {
+ if (!wedge && ordered) {
assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
assertTrue(mQueue.getPreferredSchedulingGroupLocked(r)
!= ProcessList.SCHED_GROUP_UNDEFINED);
@@ -449,6 +504,13 @@
any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
}
+ private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app, Intent intent)
+ throws Exception {
+ verify(app.getThread(), mode).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ }
+
private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app, Intent intent,
ComponentName component) throws Exception {
final Intent targetedIntent = new Intent(intent);
@@ -689,7 +751,8 @@
@Test
public void testWedged() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
- final ProcessRecord receiverApp = makeWedgedActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+ ProcessBehavior.WEDGE);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
@@ -700,6 +763,106 @@
}
/**
+ * Verify that we handle registered receivers in a process that always
+ * responds with {@link DeadObjectException}, recovering to restart the
+ * process and deliver their next broadcast.
+ */
+ @Test
+ public void testDead_Registered() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+ ProcessBehavior.DEAD);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeRegisteredReceiver(receiverApp))));
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+ waitForIdle();
+
+ // First broadcast should have already been dead
+ verifyScheduleRegisteredReceiver(receiverApp, airplane);
+ verify(receiverApp).scheduleCrashLocked(any(),
+ eq(CannotDeliverBroadcastException.TYPE_ID), any());
+
+ // Second broadcast in new process should work fine
+ final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ assertNotEquals(receiverApp, restartedReceiverApp);
+ verifyScheduleReceiver(restartedReceiverApp, timezone);
+ }
+
+ /**
+ * Verify that we handle manifest receivers in a process that always
+ * responds with {@link DeadObjectException}, recovering to restart the
+ * process and deliver their next broadcast.
+ */
+ @Test
+ public void testDead_Manifest() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+ ProcessBehavior.DEAD);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+ waitForIdle();
+
+ // First broadcast should have already been dead
+ verifyScheduleReceiver(receiverApp, airplane);
+ verify(receiverApp).scheduleCrashLocked(any(),
+ eq(CannotDeliverBroadcastException.TYPE_ID), any());
+
+ // Second broadcast in new process should work fine
+ final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ assertNotEquals(receiverApp, restartedReceiverApp);
+ verifyScheduleReceiver(restartedReceiverApp, timezone);
+ }
+
+ /**
+ * Verify that we handle the system failing to start a process.
+ */
+ @Test
+ public void testFailStartProcess() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ // Send broadcast while process starts are failing
+ mFailStartProcess = true;
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+ // Confirm that queue goes idle, with no processes
+ waitForIdle();
+ assertEquals(1, mActiveProcesses.size());
+
+ // Send more broadcasts with working process starts
+ mFailStartProcess = false;
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+ // Confirm that we only saw second broadcast
+ waitForIdle();
+ assertEquals(3, mActiveProcesses.size());
+ final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+ getUidForPackage(PACKAGE_YELLOW));
+ verifyScheduleReceiver(never(), receiverGreenApp, airplane);
+ verifyScheduleReceiver(never(), receiverYellowApp, airplane);
+ verifyScheduleReceiver(times(1), receiverGreenApp, timezone);
+ verifyScheduleReceiver(times(1), receiverYellowApp, timezone);
+ }
+
+ /**
* Verify that we cleanup a disabled component, skipping a pending dispatch
* of broadcast to that component.
*/
@@ -742,6 +905,45 @@
}
/**
+ * Verify that killing a running process skips registered receivers.
+ */
+ @Test
+ public void testKill() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord oldApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ try (SyncBarrier b = new SyncBarrier()) {
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+ List.of(makeRegisteredReceiver(oldApp),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
+
+ synchronized (mAms) {
+ oldApp.killLocked(TAG, 42, false);
+ mQueue.onApplicationCleanupLocked(oldApp);
+ }
+ }
+ waitForIdle();
+
+ // Confirm that we cold-started after the kill
+ final ProcessRecord newApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ assertNotEquals(oldApp, newApp);
+
+ // Confirm that we saw no registered receiver traffic
+ final IApplicationThread oldThread = oldApp.getThread();
+ verify(oldThread, never()).scheduleRegisteredReceiver(any(),
+ any(), anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyInt(), anyInt());
+ final IApplicationThread newThread = newApp.getThread();
+ verify(newThread, never()).scheduleRegisteredReceiver(any(),
+ any(), anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyInt(), anyInt());
+
+ // Confirm that we saw final manifest broadcast
+ verifyScheduleReceiver(times(1), newApp, airplane,
+ new ComponentName(PACKAGE_GREEN, CLASS_GREEN));
+ }
+
+ /**
* Verify that we skip broadcasts to an app being backed up.
*/
@Test
@@ -770,13 +972,13 @@
// Purposefully warm-start the middle apps to make sure we dispatch to
// both cold and warm apps in expected order
makeActiveProcessRecord(makeApplicationInfo(PACKAGE_BLUE), PACKAGE_BLUE,
- false, false, (extras) -> {
+ ProcessBehavior.NORMAL, (extras) -> {
extras = clone(extras);
extras.putBoolean(PACKAGE_BLUE, true);
return extras;
});
makeActiveProcessRecord(makeApplicationInfo(PACKAGE_YELLOW), PACKAGE_YELLOW,
- false, false, (extras) -> {
+ ProcessBehavior.NORMAL, (extras) -> {
extras = clone(extras);
extras.putBoolean(PACKAGE_YELLOW, true);
return extras;
@@ -858,7 +1060,7 @@
// Create a process that aborts any ordered broadcasts
makeActiveProcessRecord(makeApplicationInfo(PACKAGE_GREEN), PACKAGE_GREEN,
- false, true, (extras) -> {
+ ProcessBehavior.ABORT, (extras) -> {
extras = clone(extras);
extras.putBoolean(PACKAGE_GREEN, true);
return extras;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 5d9d765..6b8c26d 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -25,7 +25,6 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -39,6 +38,7 @@
import androidx.test.InstrumentationRegistry;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index ef203d0..57ded99 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -56,7 +56,6 @@
import android.graphics.Point;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -84,6 +83,7 @@
import com.android.internal.app.BlockedAppStreamingActivity;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 8280fc6..fc2a4cf 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -38,7 +38,6 @@
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -56,7 +55,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessControllerTest {
private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index 6a6cd6c..800f60b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -35,7 +35,6 @@
import android.os.Temperature;
import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -57,7 +56,6 @@
import java.util.List;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class BrightnessThrottlerTest {
private static final float EPSILON = 0.000001f;
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
index 26a83a2..53d8de0c 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -39,7 +38,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class ColorFadeTest {
private static final int DISPLAY_ID = 123;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 66420ad..04702c4 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -27,7 +27,6 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -44,7 +43,6 @@
import java.nio.file.Path;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class DisplayDeviceConfigTest {
private DisplayDeviceConfig mDisplayDeviceConfig;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3eb1dea..3c7bb2a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -53,12 +53,10 @@
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
-import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
import android.os.Process;
-import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
@@ -78,6 +76,7 @@
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -106,7 +105,6 @@
import java.util.stream.LongStream;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 53fa3e2..a1e5ce7 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -22,14 +22,10 @@
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
-
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
-import static com.android.server.display.AutomaticBrightnessController
- .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
-
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
-
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static org.junit.Assert.assertEquals;
@@ -51,7 +47,6 @@
import android.os.Temperature;
import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import android.test.mock.MockContentResolver;
import android.util.MathUtils;
@@ -76,7 +71,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class HighBrightnessModeControllerTest {
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index cc68ba8..0b33c30 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -45,7 +45,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
@@ -67,7 +66,6 @@
import java.util.Set;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class LogicalDisplayMapperTest {
private static int sUniqueTestDisplayId = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index b0738fd..5a43530 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -26,7 +26,6 @@
import android.app.PropertyInvalidatedCache;
import android.graphics.Point;
-import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -40,7 +39,6 @@
import java.io.OutputStream;
@SmallTest
-@Presubmit
public class LogicalDisplayTest {
private static final int DISPLAY_ID = 0;
private static final int LAYER_STACK = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
index 9f1a209..92d8abd 100644
--- a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
@@ -3,18 +3,10 @@
{
"name": "FrameworksServicesTests",
"options": [
- {
- "include-filter": "com.android.server.display."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
+ {"include-filter": "com.android.server.display"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
]
}
]
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
index f69c5c2..fabf535 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import android.hardware.display.BrightnessInfo;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +28,6 @@
import org.junit.runner.RunWith;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class BrightnessEventTest {
private BrightnessEvent mBrightnessEvent;
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index ffc2e0d..57aa61a 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertEquals;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -28,7 +27,6 @@
import org.junit.runner.RunWith;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class BrightnessReasonTest {
private BrightnessReason mBrightnessReason;
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index 844f5d4..e390bcc 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -19,13 +19,14 @@
import android.content.Context
import android.content.ContextWrapper
import android.hardware.display.DisplayViewport
-import android.hardware.input.InputManagerInternal
import android.os.IInputConstants
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.Display
import android.view.PointerIcon
import androidx.test.InstrumentationRegistry
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -35,7 +36,6 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
@@ -43,9 +43,8 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
/**
* Tests for {@link InputManagerService}.
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 90b19a4..04ba7d3 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -35,13 +35,13 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
import androidx.annotation.NonNull;
import com.android.server.LocalServices;
import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 2918365..107bbe1 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -79,7 +79,10 @@
<activity android:name="com.android.server.wm.ActivityOptionsTest$MainActivity"
android:turnScreenOn="true"
android:showWhenLocked="true" />
- <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity" />
+ <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:turnScreenOn="true"
+ android:showWhenLocked="true"/>
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
diff --git a/services/tests/wmtests/res/values/styles.xml b/services/tests/wmtests/res/values/styles.xml
new file mode 100644
index 0000000..6857ff99
--- /dev/null
+++ b/services/tests/wmtests/res/values/styles.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ 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.
+ -->
+
+<resources>
+ <style name="WhiteBackgroundTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+</resources>
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index fe46c14..a76b82b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -58,7 +58,6 @@
import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -76,6 +75,7 @@
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 333be7b..2f23e7f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2990,7 +2990,8 @@
.setSystemDecorations(true).build();
// Add a decor insets provider window.
final WindowState navbar = createNavBarWithProvidedInsets(squareDisplay);
- squareDisplay.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(navbar);
+ assertTrue(navbar.providesNonDecorInsets()
+ && squareDisplay.getDisplayPolicy().updateDecorInsetsInfo());
squareDisplay.sendNewConfiguration();
final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index e2c94c5..49fd1ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -296,6 +296,14 @@
}
@Test
+ public void testRemoveTask_stopsRecording_nullSessionShouldNotThrowExceptions() {
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+ mContentRecorder.updateRecording();
+ mContentRecorder.setContentRecordingSession(null);
+ mTask.removeImmediately();
+ }
+
+ @Test
public void testUpdateMirroredSurface_capturedAreaResized() {
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 262b141..a980765 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -292,12 +292,16 @@
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
final DisplayInfo di = mDisplayContent.getDisplayInfo();
final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp;
- assertTrue(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
+ assertTrue(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
di.logicalWidth, di.logicalHeight).mConfigInsets.bottom);
mDisplayContent.sendNewConfiguration();
assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
- assertFalse(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
+ assertFalse(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
+
+ navbar.removeIfPossible();
+ assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
+ di.logicalHeight).mNonDecorInsets.bottom);
}
@SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index adf694c..170b388 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1532,7 +1532,7 @@
@Override
void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
- WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index 0b58428..736f8f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.statusBars;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertNotNull;
@@ -23,20 +27,33 @@
import android.app.Activity;
import android.app.Instrumentation;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.ServiceManager;
import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.IWindowManager;
import android.view.PointerIcon;
import android.view.SurfaceControl;
-import android.view.WindowManager;
+import android.view.cts.surfacevalidator.BitmapPixelChecker;
+import android.view.cts.surfacevalidator.PixelColor;
+import android.view.cts.surfacevalidator.SaveBitmapHelper;
import android.window.ScreenCapture;
+import android.window.ScreenCapture.ScreenCaptureListener;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
+import android.window.ScreenCapture.ScreenshotSync;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -45,6 +62,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -60,6 +78,8 @@
private static final int BUFFER_HEIGHT = 100;
private final Instrumentation mInstrumentation = getInstrumentation();
+ @Rule
+ public TestName mTestName = new TestName();
@Rule
public ActivityTestRule<ScreenshotActivity> mActivityRule =
@@ -95,8 +115,8 @@
buffer.unlockCanvasAndPost(canvas);
t.show(secureSC)
- .setBuffer(secureSC, buffer)
- .setColorSpace(secureSC, ColorSpace.get(ColorSpace.Named.SRGB))
+ .setBuffer(secureSC, HardwareBuffer.createFromGraphicBuffer(buffer))
+ .setDataSpace(secureSC, DataSpace.DATASPACE_SRGB)
.apply(true);
ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC)
@@ -112,15 +132,70 @@
Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
screenshot.recycle();
- int numMatchingPixels = PixelChecker.getNumMatchingPixels(swBitmap,
- new PixelColor(PixelColor.RED));
- long sizeOfBitmap = swBitmap.getWidth() * swBitmap.getHeight();
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
+ Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int sizeOfBitmap = bounds.width() * bounds.height();
boolean success = numMatchingPixels == sizeOfBitmap;
swBitmap.recycle();
assertTrue(success);
}
+ @Test
+ public void testCaptureDisplay() throws Exception {
+ IWindowManager windowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService(Context.WINDOW_SERVICE));
+ SurfaceControl sc = new SurfaceControl.Builder()
+ .setName("Layer")
+ .setCallsite("testCaptureDisplay")
+ .build();
+
+ SurfaceControl.Transaction t = mActivity.addChildSc(sc);
+ mInstrumentation.waitForIdleSync();
+
+ GraphicBuffer buffer = GraphicBuffer.create(BUFFER_WIDTH, BUFFER_HEIGHT,
+ PixelFormat.RGBA_8888,
+ GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
+ | GraphicBuffer.USAGE_SW_WRITE_RARELY);
+
+ Canvas canvas = buffer.lockCanvas();
+ canvas.drawColor(Color.RED);
+ buffer.unlockCanvasAndPost(canvas);
+
+ Point point = mActivity.getPositionBelowStatusBar();
+ t.show(sc)
+ .setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer))
+ .setDataSpace(sc, DataSpace.DATASPACE_SRGB)
+ .setPosition(sc, point.x, point.y)
+ .apply(true);
+
+ Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture =
+ ScreenCapture.createSyncCaptureListener();
+ windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture.first);
+ ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.second.get();
+ assertNotNull(hardwareBuffer);
+
+ Bitmap screenshot = hardwareBuffer.asBitmap();
+ assertNotNull(screenshot);
+
+ Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+ screenshot.recycle();
+
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
+ Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x, BUFFER_HEIGHT + point.y);
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int pixelMatchSize = bounds.width() * bounds.height();
+ boolean success = numMatchingPixels == pixelMatchSize;
+
+ if (!success) {
+ SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
+ }
+ swBitmap.recycle();
+ assertTrue("numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
+ success);
+ }
+
public static class ScreenshotActivity extends Activity {
private static final long WAIT_TIMEOUT_S = 5;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -130,7 +205,6 @@
super.onCreate(savedInstanceState);
getWindow().getDecorView().setPointerIcon(
PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
@@ -148,88 +222,14 @@
}
return t;
}
- }
- public abstract static class PixelChecker {
- static int getNumMatchingPixels(Bitmap bitmap, PixelColor pixelColor) {
- int numMatchingPixels = 0;
- for (int x = 0; x < bitmap.getWidth(); x++) {
- for (int y = 0; y < bitmap.getHeight(); y++) {
- int color = bitmap.getPixel(x, y);
- if (matchesColor(pixelColor, color)) {
- numMatchingPixels++;
- }
- }
- }
- return numMatchingPixels;
- }
+ public Point getPositionBelowStatusBar() {
+ Insets statusBarInsets = getWindow()
+ .getDecorView()
+ .getRootWindowInsets()
+ .getInsets(statusBars() | displayCutout());
- static boolean matchesColor(PixelColor expectedColor, int color) {
- final float red = Color.red(color);
- final float green = Color.green(color);
- final float blue = Color.blue(color);
- final float alpha = Color.alpha(color);
-
- return alpha <= expectedColor.mMaxAlpha
- && alpha >= expectedColor.mMinAlpha
- && red <= expectedColor.mMaxRed
- && red >= expectedColor.mMinRed
- && green <= expectedColor.mMaxGreen
- && green >= expectedColor.mMinGreen
- && blue <= expectedColor.mMaxBlue
- && blue >= expectedColor.mMinBlue;
- }
- }
-
- public static class PixelColor {
- public static final int BLACK = 0xFF000000;
- public static final int RED = 0xFF0000FF;
- public static final int GREEN = 0xFF00FF00;
- public static final int BLUE = 0xFFFF0000;
- public static final int YELLOW = 0xFF00FFFF;
- public static final int MAGENTA = 0xFFFF00FF;
- public static final int WHITE = 0xFFFFFFFF;
-
- public static final int TRANSPARENT_RED = 0x7F0000FF;
- public static final int TRANSPARENT_BLUE = 0x7FFF0000;
- public static final int TRANSPARENT = 0x00000000;
-
- // Default to black
- public short mMinAlpha;
- public short mMaxAlpha;
- public short mMinRed;
- public short mMaxRed;
- public short mMinBlue;
- public short mMaxBlue;
- public short mMinGreen;
- public short mMaxGreen;
-
- public PixelColor(int color) {
- short alpha = (short) ((color >> 24) & 0xFF);
- short blue = (short) ((color >> 16) & 0xFF);
- short green = (short) ((color >> 8) & 0xFF);
- short red = (short) (color & 0xFF);
-
- mMinAlpha = (short) getMinValue(alpha);
- mMaxAlpha = (short) getMaxValue(alpha);
- mMinRed = (short) getMinValue(red);
- mMaxRed = (short) getMaxValue(red);
- mMinBlue = (short) getMinValue(blue);
- mMaxBlue = (short) getMaxValue(blue);
- mMinGreen = (short) getMinValue(green);
- mMaxGreen = (short) getMaxValue(green);
- }
-
- public PixelColor() {
- this(BLACK);
- }
-
- private int getMinValue(short color) {
- return Math.max(color - 4, 0);
- }
-
- private int getMaxValue(short color) {
- return Math.min(color + 4, 0xFF);
+ return new Point(statusBarInsets.left, statusBarInsets.top);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 46b4b76..8b63904 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -41,6 +41,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -68,6 +69,7 @@
import android.view.View;
import android.view.WindowManager;
import android.window.ClientWindowFrames;
+import android.window.ScreenCapture;
import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
@@ -423,6 +425,45 @@
LETTERBOX_BACKGROUND_SOLID_COLOR)).isFalse();
}
+ @Test
+ public void testCaptureDisplay() {
+ Rect displayBounds = new Rect(0, 0, 100, 200);
+ spyOn(mDisplayContent);
+ when(mDisplayContent.getBounds()).thenReturn(displayBounds);
+
+ // Null captureArgs
+ ScreenCapture.LayerCaptureArgs resultingArgs =
+ mWm.getCaptureArgs(DEFAULT_DISPLAY, null /* captureArgs */);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, didn't set rect
+ ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>().build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, invalid rect
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .setSourceCrop(new Rect(0, 0, -1, -1))
+ .build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, null rect
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .setSourceCrop(null)
+ .build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, valid rect
+ Rect validRect = new Rect(0, 0, 10, 50);
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .setSourceCrop(validRect)
+ .build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(validRect, resultingArgs.mSourceCrop);
+ }
+
private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
when(remoteToken.toWindowContainerToken()).thenReturn(wct);
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 1d6798b..a0467c2 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -856,6 +856,31 @@
public Builder() {}
/**
+ * Builder from the existing {@link NetworkRegistrationInfo}.
+ *
+ * @param nri The network registration info object.
+ * @hide
+ */
+ public Builder(@NonNull NetworkRegistrationInfo nri) {
+ mDomain = nri.mDomain;
+ mTransportType = nri.mTransportType;
+ mInitialRegistrationState = nri.mInitialRegistrationState;
+ mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+ mRejectCause = nri.mRejectCause;
+ mEmergencyOnly = nri.mEmergencyOnly;
+ mAvailableServices = new ArrayList<>(nri.mAvailableServices);
+ mCellIdentity = nri.mCellIdentity;
+ if (nri.mDataSpecificInfo != null) {
+ mDataSpecificRegistrationInfo = new DataSpecificRegistrationInfo(
+ nri.mDataSpecificInfo);
+ }
+ if (nri.mVoiceSpecificInfo != null) {
+ mVoiceSpecificRegistrationInfo = new VoiceSpecificRegistrationInfo(
+ nri.mVoiceSpecificInfo);
+ }
+ }
+
+ /**
* Set the network domain.
*
* @param domain Network domain.
diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp
index ddbf168..7fbe3b3 100644
--- a/tests/Codegen/Android.bp
+++ b/tests/Codegen/Android.bp
@@ -24,6 +24,14 @@
plugins: [
"staledataclass-annotation-processor",
],
+ // Exports needed for staledataclass-annotation-processor, see b/139342589.
+ javacflags: [
+ "-J--add-modules=jdk.compiler",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
static_libs: [
"junit",
"hamcrest",
diff --git a/tests/HandwritingIme/OWNERS b/tests/HandwritingIme/OWNERS
new file mode 100644
index 0000000..6bb4b17
--- /dev/null
+++ b/tests/HandwritingIme/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index a415217..bba819c 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -82,8 +82,16 @@
if (attr1[i].name != attr2[i].name) {
return false
}
- val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
- val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+ val value1 = attr1[i].value
+ val value2 = attr2[i].value
+ if (value1 == null && value2 == null) {
+ continue
+ }
+ if (value1 == null || value2 == null) {
+ return false
+ }
+ val v1 = ConstantEvaluator.evaluate(context, value1)
+ val v2 = ConstantEvaluator.evaluate(context, value2)
if (v1 != v2) {
return false
}
diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp
index 1e50976..2169c49 100644
--- a/tools/processors/staledataclass/Android.bp
+++ b/tools/processors/staledataclass/Android.bp
@@ -22,17 +22,13 @@
static_libs: [
"codegen-version-info",
],
- // The --add-modules/exports flags below don't work for kotlinc yet, so pin this module to Java language level 8 (see b/139342589):
- java_version: "1.8",
- openjdk9: {
- javacflags: [
- "--add-modules=jdk.compiler",
- "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
- ],
- },
+ javacflags: [
+ "--add-modules=jdk.compiler",
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
use_tools_jar: true,
}
diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
index 27a8853..1cef5b0 100644
--- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
+++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
package android.processor.staledataclass