Merge "Add EnergyConsumer data to battery history"
diff --git a/Android.bp b/Android.bp
index c0a70b9..2c550fd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -428,6 +428,15 @@
name: "framework-minus-apex-intdefs",
defaults: ["framework-minus-apex-defaults"],
plugins: ["intdef-annotation-processor"],
+
+ // Errorprone and android lint will already run on framework-minus-apex, don't rerun them on
+ // the intdefs version in order to speed up the build.
+ errorprone: {
+ enabled: false,
+ },
+ lint: {
+ enabled: false,
+ },
}
// This "framework" module is NOT installed to the device. It's
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 1d92778..9a4323a 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -25,6 +25,6 @@
hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
-ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/ktfmt_includes.txt ${PREUPLOAD_FILES}
+ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/packages/SystemUI/ktfmt_includes.txt ${PREUPLOAD_FILES}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 3781b6d..03e5468 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -257,7 +257,7 @@
runShellCommand(String.format(
"cmd blob_store delete-blob --algo %s --digest %s --label %s --expiry %d --tag %s",
blobHandle.algorithm,
- Base64.getEncoder().encode(blobHandle.digest),
+ Base64.getEncoder().encodeToString(blobHandle.digest),
blobHandle.label,
blobHandle.expiryTimeMillis,
blobHandle.tag));
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
index 0f3a068..53e5d99 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidTest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
@@ -44,7 +44,7 @@
<option name="hidden-api-checks" value="false"/>
<!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
- <option name="device-listeners" value="android.device.collectors.PerfettoListener,android.device.collectors.SimpleperfListener" />
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener,android.device.collectors.SimpleperfListener" />
<!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
<option name="instrumentation-arg" key="newRunListenerMode" value="true" />
@@ -58,7 +58,15 @@
<option name="instrumentation-arg" key="arguments" value="""" />
<option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" />
<option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger" />
- <option name="instrumentation-arg" key="symbols_to_report" value=""android::SurfaceFlinger::commit(long, long, long)"" />
+ <option name="instrumentation-arg" key="symbols_to_report" value=""android::SurfaceFlinger::commit(;android::SurfaceFlinger::composite("" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
index f4d0c05..45d164c 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
@@ -16,6 +16,7 @@
package android.surfaceflinger;
+import android.graphics.Bitmap;
import android.graphics.Color;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
@@ -23,14 +24,19 @@
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.Random;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
public class SurfaceFlingerPerfTest {
@@ -48,19 +54,175 @@
public void setup() {
mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
}
+
+ @After
+ public void teardown() {
+ mSurfaceControls.forEach(SurfaceControl::release);
+ mByfferTrackers.forEach(BufferFlinger::freeBuffers);
+ }
+
+
+ private ArrayList<BufferFlinger> mByfferTrackers = new ArrayList<>();
+ private BufferFlinger createBufferTracker(int color) {
+ BufferFlinger bufferTracker = new BufferFlinger(BUFFER_COUNT, color);
+ mByfferTrackers.add(bufferTracker);
+ return bufferTracker;
+ }
+
+ private ArrayList<SurfaceControl> mSurfaceControls = new ArrayList<>();
+ private SurfaceControl createSurfaceControl() throws InterruptedException {
+ SurfaceControl sc = mActivity.createChildSurfaceControl();
+ mSurfaceControls.add(sc);
+ return sc;
+ }
+
@Test
- public void submitSingleBuffer() throws Exception {
- SurfaceControl sc = mActivity.getChildSurfaceControl();
+ public void singleBuffer() throws Exception {
+ SurfaceControl sc = createSurfaceControl();
+ BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- BufferFlinger bufferflinger = new BufferFlinger(BUFFER_COUNT, Color.GREEN);
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ bufferTracker.addBuffer(t, sc);
t.show(sc);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- bufferflinger.addBuffer(t, sc);
+ bufferTracker.addBuffer(t, sc);
t.apply();
}
- bufferflinger.freeBuffers();
}
-}
+ static int getRandomColorComponent() {
+ return new Random().nextInt(155) + 100;
+ }
+
+ @Test
+ public void multipleBuffers() throws Exception {
+ final int MAX_BUFFERS = 10;
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ SurfaceControl sc = createSurfaceControl();
+ BufferFlinger bufferTracker = createBufferTracker(Color.argb(getRandomColorComponent(),
+ getRandomColorComponent(), getRandomColorComponent(),
+ getRandomColorComponent()));
+ bufferTracker.addBuffer(t, sc);
+ t.setPosition(sc, i * 10, i * 10);
+ t.show(sc);
+ }
+ t.apply(true);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ mByfferTrackers.get(i).addBuffer(t, mSurfaceControls.get(i));
+ }
+ t.apply();
+ }
+ }
+
+ @Test
+ public void multipleOpaqueBuffers() throws Exception {
+ final int MAX_BUFFERS = 10;
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ SurfaceControl sc = createSurfaceControl();
+ BufferFlinger bufferTracker = createBufferTracker(Color.rgb(getRandomColorComponent(),
+ getRandomColorComponent(), getRandomColorComponent()));
+ bufferTracker.addBuffer(t, sc);
+ t.setOpaque(sc, true);
+ t.setPosition(sc, i * 10, i * 10);
+ t.show(sc);
+ }
+ t.apply(true);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ mByfferTrackers.get(i).addBuffer(t, mSurfaceControls.get(i));
+ }
+ t.apply();
+ }
+ }
+
+ @Test
+ public void geometryChanges() throws Exception {
+ final int MAX_POSITION = 10;
+ final float MAX_SCALE = 2.0f;
+
+ SurfaceControl sc = createSurfaceControl();
+ BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ bufferTracker.addBuffer(t, sc);
+ t.show(sc).apply(true);
+
+ int step = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ step = ++step % MAX_POSITION;
+ t.setPosition(sc, step, step);
+ float scale = ((step * MAX_SCALE) / MAX_POSITION) + 0.5f;
+ t.setScale(sc, scale, scale);
+ t.apply();
+ }
+ }
+
+ @Test
+ public void geometryWithBufferChanges() throws Exception {
+ final int MAX_POSITION = 10;
+
+ SurfaceControl sc = createSurfaceControl();
+ BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ bufferTracker.addBuffer(t, sc);
+ t.show(sc).apply(true);
+
+ int step = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ step = ++step % MAX_POSITION;
+ t.setPosition(sc, step, step);
+ float scale = ((step * 2.0f) / MAX_POSITION) + 0.5f;
+ t.setScale(sc, scale, scale);
+ bufferTracker.addBuffer(t, sc);
+ t.apply();
+ }
+ }
+
+ @Test
+ public void addRemoveLayers() throws Exception {
+ SurfaceControl sc = createSurfaceControl();
+ BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ SurfaceControl childSurfaceControl = new SurfaceControl.Builder()
+ .setName("childLayer").setBLASTLayer().build();
+ bufferTracker.addBuffer(t, childSurfaceControl);
+ t.reparent(childSurfaceControl, sc);
+ t.apply();
+ t.remove(childSurfaceControl).apply();
+ }
+ }
+
+ @Test
+ public void displayScreenshot() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Bitmap screenshot =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot();
+ screenshot.recycle();
+ }
+ }
+
+ @Test
+ public void layerScreenshot() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Bitmap screenshot =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot(
+ mActivity.getWindow());
+ screenshot.recycle();
+ }
+ }
+
+}
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
index a9b2a31..832a0cd 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
@@ -43,7 +43,7 @@
setContentView(mTestSurfaceView);
}
- public SurfaceControl getChildSurfaceControl() throws InterruptedException {
+ public SurfaceControl createChildSurfaceControl() throws InterruptedException {
return mTestSurfaceView.getChildSurfaceControlHelper();
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index f49cdbf..ab0ac5a 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1312,6 +1312,9 @@
* Calling this method will override any requirements previously defined
* by {@link #setRequiredNetwork(NetworkRequest)}; you typically only
* want to call one of these methods.
+ * <p> Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link JobScheduler} may try to shift the execution of jobs requiring
+ * {@link #NETWORK_TYPE_ANY} to when there is access to an un-metered network.
* <p class="note">
* When your job executes in
* {@link JobService#onStartJob(JobParameters)}, be sure to use the
@@ -1742,6 +1745,7 @@
* <li>Bypass Doze, app standby, and battery saver network restrictions</li>
* <li>Be less likely to be killed than regular jobs</li>
* <li>Be subject to background location throttling</li>
+ * <li>Be exempt from delay to optimize job execution</li>
* </ol>
*
* <p>
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 632ecb2c..dfdb290 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -45,8 +45,23 @@
* </p>
* <p>
* The framework will be intelligent about when it executes jobs, and attempt to batch
- * and defer them as much as possible. Typically if you don't specify a deadline on a job, it
+ * and defer them as much as possible. Typically, if you don't specify a deadline on a job, it
* can be run at any moment depending on the current state of the JobScheduler's internal queue.
+ * </p>
+ * <p>
+ * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * JobScheduler may try to optimize job execution by shifting execution to times with more available
+ * system resources in order to lower user impact. Factors in system health include sufficient
+ * battery, idle, charging, and access to an un-metered network. Jobs will initially be treated as
+ * if they have all these requirements, but as their deadlines approach, restrictions will become
+ * less strict. Requested requirements will not be affected by this change.
+ * </p>
+ *
+ * {@see android.app.job.JobInfo.Builder#setRequiresBatteryNotLow(boolean)}
+ * {@see android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}
+ * {@see android.app.job.JobInfo.Builder#setRequiresCharging(boolean)}
+ * {@see android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}
+ *
* <p>
* While a job is running, the system holds a wakelock on behalf of your app. For this reason,
* you do not need to take any action to guarantee that the device stays awake for the
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 448a808..3ce9e6d 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -765,6 +765,7 @@
return;
}
synchronized (mLock) {
+ mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
loadInstalledPackageListLocked();
final boolean isFirstSetup = !mScribe.recordExists();
if (isFirstSetup) {
@@ -803,10 +804,7 @@
if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
return;
}
- synchronized (mLock) {
- mHandler.post(this::setupHeavyWork);
- mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
- }
+ mHandler.post(this::setupHeavyWork);
}
private void onBootPhaseBootCompleted() {
diff --git a/cmds/abx/Android.bp b/cmds/abx/Android.bp
index 50a0b75..a832dea 100644
--- a/cmds/abx/Android.bp
+++ b/cmds/abx/Android.bp
@@ -1,4 +1,3 @@
-
package {
default_applicable_licenses: ["frameworks_base_cmds_abx_license"],
}
@@ -18,7 +17,7 @@
java_binary {
name: "abx",
- wrapper: "abx",
+ wrapper: "abx.sh",
srcs: ["**/*.java"],
required: [
"abx2xml",
@@ -28,10 +27,10 @@
sh_binary {
name: "abx2xml",
- src: "abx2xml",
+ src: "abx2xml.sh",
}
sh_binary {
name: "xml2abx",
- src: "xml2abx",
+ src: "xml2abx.sh",
}
diff --git a/cmds/abx/abx b/cmds/abx/abx.sh
similarity index 100%
rename from cmds/abx/abx
rename to cmds/abx/abx.sh
diff --git a/cmds/abx/abx2xml b/cmds/abx/abx2xml
deleted file mode 100755
index 0a9362d..0000000
--- a/cmds/abx/abx2xml
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/system/bin/sh
-export CLASSPATH=/system/framework/abx.jar
-exec app_process /system/bin com.android.commands.abx.Abx "$0" "$@"
diff --git a/cmds/abx/abx b/cmds/abx/abx2xml.sh
similarity index 100%
copy from cmds/abx/abx
copy to cmds/abx/abx2xml.sh
diff --git a/cmds/abx/xml2abx b/cmds/abx/xml2abx
deleted file mode 100755
index 0a9362d..0000000
--- a/cmds/abx/xml2abx
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/system/bin/sh
-export CLASSPATH=/system/framework/abx.jar
-exec app_process /system/bin com.android.commands.abx.Abx "$0" "$@"
diff --git a/cmds/abx/abx b/cmds/abx/xml2abx.sh
similarity index 100%
copy from cmds/abx/abx
copy to cmds/abx/xml2abx.sh
diff --git a/cmds/bmgr/Android.bp b/cmds/bmgr/Android.bp
index 14beb55..a85669a 100644
--- a/cmds/bmgr/Android.bp
+++ b/cmds/bmgr/Android.bp
@@ -20,6 +20,6 @@
java_binary {
name: "bmgr",
- wrapper: "bmgr",
+ wrapper: "bmgr.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/bmgr/bmgr b/cmds/bmgr/bmgr.sh
similarity index 100%
rename from cmds/bmgr/bmgr
rename to cmds/bmgr/bmgr.sh
diff --git a/cmds/bu/Android.bp b/cmds/bu/Android.bp
index 5b4ec31..b61a7a6 100644
--- a/cmds/bu/Android.bp
+++ b/cmds/bu/Android.bp
@@ -20,6 +20,6 @@
java_binary {
name: "bu",
- wrapper: "bu",
+ wrapper: "bu.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/bu/bu b/cmds/bu/bu.sh
similarity index 100%
rename from cmds/bu/bu
rename to cmds/bu/bu.sh
diff --git a/cmds/content/Android.bp b/cmds/content/Android.bp
index c70d01e..0dd0f03 100644
--- a/cmds/content/Android.bp
+++ b/cmds/content/Android.bp
@@ -19,6 +19,6 @@
java_binary {
name: "content",
- wrapper: "content",
+ wrapper: "content.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/content/content b/cmds/content/content.sh
similarity index 100%
rename from cmds/content/content
rename to cmds/content/content.sh
diff --git a/cmds/device_config/Android.bp b/cmds/device_config/Android.bp
index 69572d8..83b2730 100644
--- a/cmds/device_config/Android.bp
+++ b/cmds/device_config/Android.bp
@@ -14,5 +14,5 @@
sh_binary {
name: "device_config",
- src: "device_config",
+ src: "device_config.sh",
}
diff --git a/cmds/device_config/device_config b/cmds/device_config/device_config.sh
similarity index 100%
rename from cmds/device_config/device_config
rename to cmds/device_config/device_config.sh
diff --git a/cmds/dpm/Android.bp b/cmds/dpm/Android.bp
index 6819d09..29bee49 100644
--- a/cmds/dpm/Android.bp
+++ b/cmds/dpm/Android.bp
@@ -20,5 +20,5 @@
sh_binary {
name: "dpm",
- src: "dpm",
+ src: "dpm.sh",
}
diff --git a/cmds/dpm/dpm b/cmds/dpm/dpm.sh
similarity index 100%
rename from cmds/dpm/dpm
rename to cmds/dpm/dpm.sh
diff --git a/cmds/hid/Android.bp b/cmds/hid/Android.bp
index 295c71c..a6e2769 100644
--- a/cmds/hid/Android.bp
+++ b/cmds/hid/Android.bp
@@ -20,7 +20,7 @@
java_binary {
name: "hid",
- wrapper: "hid",
+ wrapper: "hid.sh",
srcs: ["**/*.java"],
required: ["libhidcommand_jni"],
}
diff --git a/cmds/hid/hid b/cmds/hid/hid.sh
similarity index 100%
rename from cmds/hid/hid
rename to cmds/hid/hid.sh
diff --git a/cmds/ime/Android.bp b/cmds/ime/Android.bp
index 6dd3ba1..5f54ffa 100644
--- a/cmds/ime/Android.bp
+++ b/cmds/ime/Android.bp
@@ -20,5 +20,5 @@
sh_binary {
name: "ime",
- src: "ime",
+ src: "ime.sh",
}
diff --git a/cmds/ime/ime b/cmds/ime/ime.sh
similarity index 100%
rename from cmds/ime/ime
rename to cmds/ime/ime.sh
diff --git a/cmds/input/Android.bp b/cmds/input/Android.bp
index 2e30176..8f44f3e 100644
--- a/cmds/input/Android.bp
+++ b/cmds/input/Android.bp
@@ -20,5 +20,5 @@
sh_binary {
name: "input",
- src: "input",
+ src: "input.sh",
}
diff --git a/cmds/input/input b/cmds/input/input.sh
similarity index 100%
rename from cmds/input/input
rename to cmds/input/input.sh
diff --git a/cmds/locksettings/Android.bp b/cmds/locksettings/Android.bp
index 3869c8f..5ee5824 100644
--- a/cmds/locksettings/Android.bp
+++ b/cmds/locksettings/Android.bp
@@ -23,6 +23,6 @@
java_binary {
name: "locksettings",
- wrapper: "locksettings",
+ wrapper: "locksettings.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/locksettings/locksettings b/cmds/locksettings/locksettings.sh
similarity index 100%
rename from cmds/locksettings/locksettings
rename to cmds/locksettings/locksettings.sh
diff --git a/cmds/pm/Android.bp b/cmds/pm/Android.bp
index 847dbab..0e61a9e 100644
--- a/cmds/pm/Android.bp
+++ b/cmds/pm/Android.bp
@@ -20,5 +20,5 @@
sh_binary {
name: "pm",
- src: "pm",
+ src: "pm.sh",
}
diff --git a/cmds/pm/pm b/cmds/pm/pm.sh
similarity index 100%
rename from cmds/pm/pm
rename to cmds/pm/pm.sh
diff --git a/cmds/requestsync/Android.bp b/cmds/requestsync/Android.bp
index 57e8dd3..8718f79 100644
--- a/cmds/requestsync/Android.bp
+++ b/cmds/requestsync/Android.bp
@@ -20,6 +20,6 @@
java_binary {
name: "requestsync",
- wrapper: "requestsync",
+ wrapper: "requestsync.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/requestsync/requestsync b/cmds/requestsync/requestsync.sh
similarity index 100%
rename from cmds/requestsync/requestsync
rename to cmds/requestsync/requestsync.sh
diff --git a/cmds/screencap/OWNERS b/cmds/screencap/OWNERS
new file mode 100644
index 0000000..89f1177
--- /dev/null
+++ b/cmds/screencap/OWNERS
@@ -0,0 +1,2 @@
+include /graphics/java/android/graphics/OWNERS
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/cmds/settings/Android.bp b/cmds/settings/Android.bp
index cc73006..8180fd6 100644
--- a/cmds/settings/Android.bp
+++ b/cmds/settings/Android.bp
@@ -14,5 +14,5 @@
sh_binary {
name: "settings",
- src: "settings",
+ src: "settings.sh",
}
diff --git a/cmds/settings/settings b/cmds/settings/settings.sh
similarity index 100%
rename from cmds/settings/settings
rename to cmds/settings/settings.sh
diff --git a/cmds/sm/Android.bp b/cmds/sm/Android.bp
index ecfacae..403022a 100644
--- a/cmds/sm/Android.bp
+++ b/cmds/sm/Android.bp
@@ -20,6 +20,6 @@
java_binary {
name: "sm",
- wrapper: "sm",
+ wrapper: "sm.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/sm/sm b/cmds/sm/sm.sh
similarity index 100%
rename from cmds/sm/sm
rename to cmds/sm/sm.sh
diff --git a/cmds/svc/Android.bp b/cmds/svc/Android.bp
index 41a3ebd..a246087 100644
--- a/cmds/svc/Android.bp
+++ b/cmds/svc/Android.bp
@@ -20,6 +20,6 @@
java_binary {
name: "svc",
- wrapper: "svc",
+ wrapper: "svc.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/svc/svc b/cmds/svc/svc.sh
similarity index 100%
rename from cmds/svc/svc
rename to cmds/svc/svc.sh
diff --git a/cmds/telecom/Android.bp b/cmds/telecom/Android.bp
index 4da79c5..be02710 100644
--- a/cmds/telecom/Android.bp
+++ b/cmds/telecom/Android.bp
@@ -20,6 +20,6 @@
java_binary {
name: "telecom",
- wrapper: "telecom",
+ wrapper: "telecom.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/telecom/telecom b/cmds/telecom/telecom.sh
similarity index 100%
rename from cmds/telecom/telecom
rename to cmds/telecom/telecom.sh
diff --git a/cmds/uiautomator/cmds/uiautomator/Android.bp b/cmds/uiautomator/cmds/uiautomator/Android.bp
index 56e2e70..5132386 100644
--- a/cmds/uiautomator/cmds/uiautomator/Android.bp
+++ b/cmds/uiautomator/cmds/uiautomator/Android.bp
@@ -25,7 +25,7 @@
java_binary {
name: "uiautomator",
- wrapper: "uiautomator",
+ wrapper: "uiautomator.sh",
srcs: ["src/**/*.java"],
static_libs: ["uiautomator.core"],
}
diff --git a/cmds/uiautomator/cmds/uiautomator/uiautomator b/cmds/uiautomator/cmds/uiautomator/uiautomator.sh
similarity index 100%
rename from cmds/uiautomator/cmds/uiautomator/uiautomator
rename to cmds/uiautomator/cmds/uiautomator/uiautomator.sh
diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp
index 260cfc7..4b08d96 100644
--- a/cmds/uinput/Android.bp
+++ b/cmds/uinput/Android.bp
@@ -20,9 +20,10 @@
java_binary {
name: "uinput",
- wrapper: "uinput",
- srcs: ["**/*.java",
- ":uinputcommand_aidl"
+ wrapper: "uinput.sh",
+ srcs: [
+ "**/*.java",
+ ":uinputcommand_aidl",
],
required: ["libuinputcommand_jni"],
}
diff --git a/cmds/uinput/uinput b/cmds/uinput/uinput.sh
similarity index 100%
rename from cmds/uinput/uinput
rename to cmds/uinput/uinput.sh
diff --git a/cmds/vr/Android.bp b/cmds/vr/Android.bp
index 8936491..61795b4c 100644
--- a/cmds/vr/Android.bp
+++ b/cmds/vr/Android.bp
@@ -20,6 +20,6 @@
java_binary {
name: "vr",
- wrapper: "vr",
+ wrapper: "vr.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/vr/vr b/cmds/vr/vr.sh
similarity index 100%
rename from cmds/vr/vr
rename to cmds/vr/vr.sh
diff --git a/cmds/wm/Android.bp b/cmds/wm/Android.bp
index cf6b019..4d00f17 100644
--- a/cmds/wm/Android.bp
+++ b/cmds/wm/Android.bp
@@ -20,5 +20,5 @@
sh_binary {
name: "wm",
- src: "wm",
+ src: "wm.sh",
}
diff --git a/cmds/wm/wm b/cmds/wm/wm.sh
similarity index 100%
rename from cmds/wm/wm
rename to cmds/wm/wm.sh
diff --git a/core/api/current.txt b/core/api/current.txt
index 2411a4e..a26ca4f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32172,6 +32172,7 @@
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectCustomSlowCalls();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectDiskReads();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectDiskWrites();
+ method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectExplicitGc();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectNetwork();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectResourceMismatches();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectUnbufferedIo();
@@ -32186,6 +32187,7 @@
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder permitDiskReads();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder permitDiskWrites();
+ method @NonNull public android.os.StrictMode.ThreadPolicy.Builder permitExplicitGc();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder permitNetwork();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder permitResourceMismatches();
method @NonNull public android.os.StrictMode.ThreadPolicy.Builder permitUnbufferedIo();
@@ -32767,6 +32769,9 @@
public final class DiskWriteViolation extends android.os.strictmode.Violation {
}
+ public final class ExplicitGcViolation extends android.os.strictmode.Violation {
+ }
+
public final class FileUriExposedViolation extends android.os.strictmode.Violation {
}
@@ -51950,6 +51955,7 @@
method public void addChild(android.view.View, int);
method public boolean canOpenPopup();
method public int describeContents();
+ method public void enableQueryFromAppProcess(@NonNull android.view.View);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(@NonNull String);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
@@ -52017,7 +52023,6 @@
method public boolean isTextEntryKey();
method public boolean isTextSelectable();
method public boolean isVisibleToUser();
- method public void makeQueryableFromAppProcess(@NonNull android.view.View);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ef8ae70..a87f2e2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -6134,6 +6134,11 @@
field public static final int ROLE_OUTPUT = 2; // 0x2
}
+ public class AudioDeviceVolumeManager {
+ ctor public AudioDeviceVolumeManager(@NonNull android.content.Context);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
+ }
+
public final class AudioFocusInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAttributes();
@@ -6453,6 +6458,33 @@
method public void onSpatializerOutputChanged(@NonNull android.media.Spatializer, @IntRange(from=0) int);
}
+ public final class VolumeInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public static android.media.VolumeInfo getDefaultVolumeInfo();
+ method public int getMaxVolumeIndex();
+ method public int getMinVolumeIndex();
+ method public int getStreamType();
+ method @Nullable public android.media.audiopolicy.AudioVolumeGroup getVolumeGroup();
+ method public int getVolumeIndex();
+ method public boolean hasStreamType();
+ method public boolean hasVolumeGroup();
+ method public boolean isMuted();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.VolumeInfo> CREATOR;
+ field public static final int INDEX_NOT_SET = -100; // 0xffffff9c
+ }
+
+ public static final class VolumeInfo.Builder {
+ ctor public VolumeInfo.Builder(int);
+ ctor public VolumeInfo.Builder(@NonNull android.media.audiopolicy.AudioVolumeGroup);
+ ctor public VolumeInfo.Builder(@NonNull android.media.VolumeInfo);
+ method @NonNull public android.media.VolumeInfo build();
+ method @NonNull public android.media.VolumeInfo.Builder setMaxVolumeIndex(int);
+ method @NonNull public android.media.VolumeInfo.Builder setMinVolumeIndex(int);
+ method @NonNull public android.media.VolumeInfo.Builder setMuted(boolean);
+ method @NonNull public android.media.VolumeInfo.Builder setVolumeIndex(int);
+ }
+
}
package android.media.audiofx {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 53014a3..368087c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -793,6 +793,7 @@
field public static final long FORCE_RESIZE_APP = 174042936L; // 0xa5faf38L
field public static final long NEVER_SANDBOX_DISPLAY_APIS = 184838306L; // 0xb0468a2L
field public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // 0xa5faf64L
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN = 218959984L; // 0xd0d1070L
field public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // 0xabf9183L
field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f;
field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
@@ -1826,10 +1827,6 @@
method public static void setViolationLogger(android.os.StrictMode.ViolationLogger);
}
- public static final class StrictMode.ThreadPolicy.Builder {
- method @NonNull public android.os.StrictMode.ThreadPolicy.Builder detectExplicitGc();
- }
-
public static final class StrictMode.ViolationInfo implements android.os.Parcelable {
ctor public StrictMode.ViolationInfo(android.os.Parcel);
ctor public StrictMode.ViolationInfo(android.os.Parcel, boolean);
@@ -2038,13 +2035,6 @@
}
-package android.os.strictmode {
-
- public final class ExplicitGcViolation extends android.os.strictmode.Violation {
- }
-
-}
-
package android.os.vibrator {
public final class PrebakedSegment extends android.os.vibrator.VibrationEffectSegment {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index b9ad595..7215987 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -753,7 +753,7 @@
/**
* Returns the vibration pattern for notifications posted to this channel. Will be ignored if
- * vibration is not enabled ({@link #shouldVibrate()}.
+ * vibration is not enabled ({@link #shouldVibrate()}).
*/
public long[] getVibrationPattern() {
return mVibration;
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 7014d69..a5a50d6 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -43,6 +43,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -1103,7 +1104,10 @@
}
try {
boolean[] res = mInteractor.supportsCommands(mContext.getOpPackageName(), commands);
- if (DEBUG) Log.d(TAG, "supportsCommands: cmds=" + commands + " res=" + res);
+ if (DEBUG) {
+ Log.d(TAG, "supportsCommands: cmds=" + Arrays.toString(commands) + " res="
+ + Arrays.toString(res));
+ }
return res;
} catch (RemoteException e) {
throw new RuntimeException("Voice interactor has died", e);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6866510..c12e26b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -10802,14 +10803,19 @@
*/
public Intent createAdminSupportIntent(@NonNull String restriction) {
throwIfParentInstance("createAdminSupportIntent");
+ Intent result = null;
if (mService != null) {
try {
- return mService.createAdminSupportIntent(restriction);
+ result = mService.createAdminSupportIntent(restriction);
+ if (result != null) {
+ result.prepareToEnterProcess(LOCAL_FLAG_FROM_SYSTEM,
+ mContext.getAttributionSource());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- return null;
+ return result;
}
/**
diff --git a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
index 24b4f4b..63c9839 100644
--- a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
+++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
@@ -203,17 +203,14 @@
return mIsEnabled == that.mIsEnabled
&& mAllowFallbackToDefaultConnection == that.mAllowFallbackToDefaultConnection
&& mNetworkId == that.mNetworkId
- && Objects.equals(mIncludedUids, that.mIncludedUids)
- && Objects.equals(mExcludedUids, that.mExcludedUids);
+ && Arrays.equals(mIncludedUids, that.mIncludedUids)
+ && Arrays.equals(mExcludedUids, that.mExcludedUids);
}
@Override
public int hashCode() {
- return ((Objects.hashCode(mIsEnabled) * 17)
- + (Objects.hashCode(mAllowFallbackToDefaultConnection) * 19)
- + (Objects.hashCode(mIncludedUids) * 23)
- + (Objects.hashCode(mExcludedUids) * 29)
- + mNetworkId * 31);
+ return Objects.hash(mIsEnabled, mAllowFallbackToDefaultConnection,
+ Arrays.hashCode(mIncludedUids), Arrays.hashCode(mExcludedUids), mNetworkId);
}
/**
diff --git a/core/java/android/app/ambientcontext/AmbientContextCallback.java b/core/java/android/app/ambientcontext/AmbientContextCallback.java
new file mode 100644
index 0000000..9133d7f
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextCallback.java
@@ -0,0 +1,47 @@
+/*
+ * 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.app.ambientcontext;
+
+import android.annotation.NonNull;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for listening to Ambient Context events and status changes. See {@link
+ * AmbientContextManager#registerObserver(AmbientContextEventRequest, AmbientContextCallback,
+ * Executor)}
+ *
+ * @hide
+ */
+public interface AmbientContextCallback {
+ /**
+ * Called when AmbientContextManager service detects events.
+ *
+ * @param events a list of detected events.
+ */
+ void onEvents(@NonNull List<AmbientContextEvent> events);
+
+ /**
+ * Called with a statusCode when
+ * {@link AmbientContextManager#registerObserver(AmbientContextEventRequest,
+ * Executor, AmbientContextCallback)} completes, to indicate if the registration is successful
+ *
+ * @param statusCode the status of the service.
+ */
+ void onRegistrationComplete(@NonNull @AmbientContextManager.StatusCode int statusCode);
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index 11e695ad..af48fde 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -59,11 +59,21 @@
*/
public static final int EVENT_SNORE = 2;
+ /**
+ * The integer indicating a double-tap event was detected.
+ * For detecting this event type, there's no specific consent activity to request access, but
+ * the consent is implied through the double tap toggle in the Settings app.
+ *
+ * @hide
+ */
+ public static final int EVENT_BACK_DOUBLE_TAP = 3;
+
/** @hide */
@IntDef(prefix = { "EVENT_" }, value = {
EVENT_UNKNOWN,
EVENT_COUGH,
EVENT_SNORE,
+ EVENT_BACK_DOUBLE_TAP,
}) public @interface EventCode {}
/** The integer indicating an unknown level. */
@@ -150,7 +160,8 @@
@IntDef(prefix = "EVENT_", value = {
EVENT_UNKNOWN,
EVENT_COUGH,
- EVENT_SNORE
+ EVENT_SNORE,
+ EVENT_BACK_DOUBLE_TAP
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -166,6 +177,8 @@
return "EVENT_COUGH";
case EVENT_SNORE:
return "EVENT_SNORE";
+ case EVENT_BACK_DOUBLE_TAP:
+ return "EVENT_BACK_DOUBLE_TAP";
default: return Integer.toHexString(value);
}
}
@@ -478,10 +491,10 @@
}
@DataClass.Generated(
- time = 1642040319323L,
+ time = 1659950304931L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
- inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
index 308c5ed..9cb1a20 100644
--- a/core/java/android/app/ambientcontext/AmbientContextManager.java
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -282,7 +282,7 @@
Preconditions.checkArgument(!resultPendingIntent.isImmutable());
try {
RemoteCallback callback = new RemoteCallback(result -> {
- int statusCode = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+ int statusCode = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
final long identity = Binder.clearCallingIdentity();
try {
executor.execute(() -> statusConsumer.accept(statusCode));
@@ -297,6 +297,72 @@
}
/**
+ * Allows app to register as a {@link AmbientContextEvent} observer. Same as {@link
+ * #registerObserver(AmbientContextEventRequest, PendingIntent, Executor, Consumer)},
+ * but use {@link AmbientContextCallback} instead of {@link PendingIntent} as a callback on
+ * detected events.
+ * Registering another observer from the same package that has already been
+ * registered will override the previous observer. If the same app previously calls
+ * {@link #registerObserver(AmbientContextEventRequest, AmbientContextCallback, Executor)},
+ * and now calls
+ * {@link #registerObserver(AmbientContextEventRequest, PendingIntent, Executor, Consumer)},
+ * the previous observer will be replaced with the new observer with the PendingIntent callback.
+ * Or vice versa.
+ *
+ * When the registration completes, a status will be returned to client through
+ * {@link AmbientContextCallback#onRegistrationComplete(int)}.
+ * If the AmbientContextManager service is not enabled yet, or the underlying detection service
+ * is not running yet, {@link AmbientContextManager#STATUS_SERVICE_UNAVAILABLE} will be
+ * returned, and the detection won't be really started.
+ * If the underlying detection service feature is not enabled, or the requested event type is
+ * not enabled yet, {@link AmbientContextManager#STATUS_NOT_SUPPORTED} will be returned, and the
+ * detection won't be really started.
+ * If there is no user consent, {@link AmbientContextManager#STATUS_ACCESS_DENIED} will be
+ * returned, and the detection won't be really started.
+ * Otherwise, it will try to start the detection. And if it starts successfully, it will return
+ * {@link AmbientContextManager#STATUS_SUCCESS}. If it fails to start the detection, then
+ * it will return {@link AmbientContextManager#STATUS_SERVICE_UNAVAILABLE}
+ * After registerObserver succeeds and when the service detects an event, the service will
+ * trigger {@link AmbientContextCallback#onEvents(List)}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void registerObserver(
+ @NonNull AmbientContextEventRequest request,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AmbientContextCallback ambientContextCallback) {
+ try {
+ IAmbientContextObserver observer = new IAmbientContextObserver.Stub() {
+ @Override
+ public void onEvents(List<AmbientContextEvent> events) throws RemoteException {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> ambientContextCallback.onEvents(events));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onRegistrationComplete(int statusCode) throws RemoteException {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ () -> ambientContextCallback.onRegistrationComplete(statusCode));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ };
+
+ mService.registerObserverWithCallback(request, mContext.getPackageName(), observer);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Unregisters the requesting app as an {@code AmbientContextEvent} observer. Unregistering an
* observer that was already unregistered or never registered will have no effect.
*/
diff --git a/core/java/android/app/ambientcontext/IAmbientContextManager.aidl b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
index 0d9ecfd..8f06e76 100644
--- a/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
+++ b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.IAmbientContextObserver;
import android.os.RemoteCallback;
/**
@@ -29,6 +30,11 @@
void registerObserver(in AmbientContextEventRequest request,
in PendingIntent resultPendingIntent,
in RemoteCallback statusCallback);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)")
+ void registerObserverWithCallback(in AmbientContextEventRequest request,
+ String packageName,
+ in IAmbientContextObserver observer);
void unregisterObserver(in String callingPackage);
void queryServiceStatus(in int[] eventTypes, in String callingPackage,
in RemoteCallback statusCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt b/core/java/android/app/ambientcontext/IAmbientContextObserver.aidl
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt
copy to core/java/android/app/ambientcontext/IAmbientContextObserver.aidl
index 88e227b..529e2fe 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt
+++ b/core/java/android/app/ambientcontext/IAmbientContextObserver.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,10 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.log.dagger
+package android.app.ambientcontext;
-import javax.inject.Qualifier
+import android.app.ambientcontext.AmbientContextEvent;
-/** A [com.android.systemui.log.LogBuffer] for KeyguardViewMediator. */
-@Qualifier
-annotation class KeyguardViewMediatorLog
\ No newline at end of file
+/**
+ * Callback interface of AmbientContextManager.
+ *
+ * @hide
+ */
+oneway interface IAmbientContextObserver {
+ void onEvents(in List<AmbientContextEvent> events);
+ void onRegistrationComplete(in int statusCode);
+}
+
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index e6091f0..2934cd2 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -204,9 +204,10 @@
@Override
public int hashCode() {
- return Objects.hash(mNamePattern, mScanFilter, mRawDataFilter, mRawDataFilterMask,
- mRenamePrefix, mRenameSuffix, mRenameBytesFrom, mRenameBytesLength,
- mRenameNameFrom, mRenameNameLength, mRenameBytesReverseOrder);
+ return Objects.hash(mNamePattern, mScanFilter, Arrays.hashCode(mRawDataFilter),
+ Arrays.hashCode(mRawDataFilterMask), mRenamePrefix, mRenameSuffix,
+ mRenameBytesFrom, mRenameBytesLength, mRenameNameFrom, mRenameNameLength,
+ mRenameBytesReverseOrder);
}
@Override
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 3f2fa21..b0c6cbc 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -495,14 +495,9 @@
@Override
public int hashCode() {
- int _hash = 1;
- _hash = 31 * _hash + mAttributionSourceState.uid;
- _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.packageName);
- _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.attributionTag);
- _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.token);
- _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.renouncedPermissions);
- _hash = 31 * _hash + Objects.hashCode(getNext());
- return _hash;
+ return Objects.hash(mAttributionSourceState.uid, mAttributionSourceState.packageName,
+ mAttributionSourceState.attributionTag, mAttributionSourceState.token,
+ Arrays.hashCode(mAttributionSourceState.renouncedPermissions), getNext());
}
@Override
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 896fe91..f593ed9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7163,12 +7163,18 @@
*/
private static final int LOCAL_FLAG_FROM_URI = 1 << 4;
+ /**
+ * Local flag indicating this instance was created by the system.
+ */
+ /** @hide */
+ public static final int LOCAL_FLAG_FROM_SYSTEM = 1 << 5;
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// toUri() and parseUri() options.
/** @hide */
- @IntDef(flag = true, prefix = { "URI_" }, value = {
+ @IntDef(flag = true, prefix = {"URI_"}, value = {
URI_ALLOW_UNSAFE,
URI_ANDROID_APP_SCHEME,
URI_INTENT_SCHEME,
@@ -10574,7 +10580,9 @@
// delivered Intent then it would have been reported when that Intent left the sending
// process.
if ((src.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0
- && (src.mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0) {
+ && (src.mLocalFlags & (
+ LOCAL_FLAG_FROM_PROTECTED_COMPONENT
+ | LOCAL_FLAG_FROM_SYSTEM)) == 0) {
mLocalFlags |= LOCAL_FLAG_UNFILTERED_EXTRAS;
}
return this;
@@ -11917,13 +11925,14 @@
// Detect cases where we're about to launch a potentially unsafe intent
if (StrictMode.vmUnsafeIntentLaunchEnabled()) {
if ((mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0
- && (mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0) {
+ && (mLocalFlags
+ & (LOCAL_FLAG_FROM_PROTECTED_COMPONENT | LOCAL_FLAG_FROM_SYSTEM)) == 0) {
StrictMode.onUnsafeIntentLaunch(this);
} else if ((mLocalFlags & LOCAL_FLAG_UNFILTERED_EXTRAS) != 0) {
StrictMode.onUnsafeIntentLaunch(this);
} else if ((mLocalFlags & LOCAL_FLAG_FROM_URI) != 0
&& !(mCategories != null && mCategories.contains(CATEGORY_BROWSABLE)
- && mComponent == null)) {
+ && mComponent == null)) {
// Since the docs for #URI_ALLOW_UNSAFE recommend setting the category to browsable
// for an implicit Intent parsed from a URI a violation should be reported if these
// conditions are not met.
@@ -11936,6 +11945,17 @@
* @hide
*/
public void prepareToEnterProcess(boolean fromProtectedComponent, AttributionSource source) {
+ if (fromProtectedComponent) {
+ prepareToEnterProcess(LOCAL_FLAG_FROM_PROTECTED_COMPONENT, source);
+ } else {
+ prepareToEnterProcess(0, source);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void prepareToEnterProcess(int localFlags, AttributionSource source) {
// We just entered destination process, so we should be able to read all
// parcelables inside.
setDefusable(true);
@@ -11943,13 +11963,15 @@
if (mSelector != null) {
// We can't recursively claim that this data is from a protected
// component, since it may have been filled in by a malicious app
- mSelector.prepareToEnterProcess(false, source);
+ mSelector.prepareToEnterProcess(0, source);
}
if (mClipData != null) {
mClipData.prepareToEnterProcess(source);
}
if (mOriginalIntent != null) {
- mOriginalIntent.prepareToEnterProcess(false, source);
+ // We can't recursively claim that this data is from a protected
+ // component, since it may have been filled in by a malicious app
+ mOriginalIntent.prepareToEnterProcess(0, source);
}
if (mContentUserHint != UserHandle.USER_CURRENT) {
@@ -11959,9 +11981,7 @@
}
}
- if (fromProtectedComponent) {
- mLocalFlags |= LOCAL_FLAG_FROM_PROTECTED_COMPONENT;
- }
+ mLocalFlags |= localFlags;
// Special attribution fix-up logic for any BluetoothDevice extras
// passed via Bluetooth intents
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 885eb70..ffd80ea 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -16,6 +16,8 @@
package android.content;
+import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
+
import android.annotation.SystemService;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
@@ -487,14 +489,19 @@
}
public Intent createLocalApprovalIntent() {
+ Intent result = null;
try {
if (mService != null) {
- return mService.createLocalApprovalIntent();
+ result = mService.createLocalApprovalIntent();
+ if (result != null) {
+ result.prepareToEnterProcess(LOCAL_FLAG_FROM_SYSTEM,
+ mContext.getAttributionSource());
+ }
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- return null;
+ return result;
}
/**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e92be7c..26c947b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1120,6 +1120,17 @@
public static final long OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN = 208648326L;
/**
+ * Overrides the min aspect ratio restriction in portrait fullscreen in order to use all
+ * available screen space.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN = 218959984L;
+
+ /**
* Compares activity window layout min width/height with require space for multi window to
* determine if it can be put into multi window mode.
*/
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index f780f81..f8c4974 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1723,7 +1723,8 @@
public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
throws RemoteException {
if (DEBUG) {
- Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
+ Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + ","
+ + Arrays.toString(packageNames));
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
@@ -1736,7 +1737,8 @@
public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
throws RemoteException {
if (DEBUG) {
- Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
+ Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + ","
+ + Arrays.toString(packageNames));
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
@@ -1750,7 +1752,8 @@
Bundle launcherExtras)
throws RemoteException {
if (DEBUG) {
- Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
+ Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + ","
+ + Arrays.toString(packageNames));
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
@@ -1763,7 +1766,8 @@
public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
throws RemoteException {
if (DEBUG) {
- Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
+ Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + ","
+ + Arrays.toString(packageNames));
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 52774e3..1f83d75 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -53,6 +53,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -2617,7 +2618,7 @@
addIndentOrComma(sb, indent);
sb.append("persons=");
- sb.append(mPersons);
+ sb.append(Arrays.toString(mPersons));
addIndentOrComma(sb, indent);
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 70b90e6..e48a02a 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -15,6 +15,8 @@
*/
package android.content.pm;
+import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
+
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -628,7 +630,12 @@
try {
mService.createShortcutResultIntent(mContext.getPackageName(),
shortcut, injectMyUserId(), ret);
- return getFutureOrThrow(ret);
+ Intent result = getFutureOrThrow(ret);
+ if (result != null) {
+ result.prepareToEnterProcess(LOCAL_FLAG_FROM_SYSTEM,
+ mContext.getAttributionSource());
+ }
+ return result;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java
index 35ad503..7bc551d 100644
--- a/core/java/android/content/res/GradientColor.java
+++ b/core/java/android/content/res/GradientColor.java
@@ -22,13 +22,6 @@
import android.annotation.Nullable;
import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources.Theme;
-
-import com.android.internal.R;
-import com.android.internal.util.GrowingArrayUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.graphics.LinearGradient;
import android.graphics.RadialGradient;
import android.graphics.Shader;
@@ -38,9 +31,16 @@
import android.util.Log;
import android.util.Xml;
+import com.android.internal.R;
+import com.android.internal.util.GrowingArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* Lets you define a gradient color, which is used inside
@@ -466,7 +466,7 @@
}
if (tempColors.length < 2) {
Log.w(TAG, "<gradient> tag requires 2 color values specified!" + tempColors.length
- + " " + tempColors);
+ + " " + Arrays.toString(tempColors));
}
if (mGradientType == GradientDrawable.LINEAR_GRADIENT) {
diff --git a/core/java/android/hardware/HardwareBuffer.aidl b/core/java/android/hardware/HardwareBuffer.aidl
index 5bdd966..1333f0d 100644
--- a/core/java/android/hardware/HardwareBuffer.aidl
+++ b/core/java/android/hardware/HardwareBuffer.aidl
@@ -16,4 +16,4 @@
package android.hardware;
-parcelable HardwareBuffer;
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h";
diff --git a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
index 55cab52..2e8e763 100644
--- a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
@@ -23,5 +23,8 @@
* @hide
*/
oneway interface IBiometricContextListener {
- void onDozeChanged(boolean isDozing);
+ // Called when doze or awake (screen on) status changes.
+ // These may be called while the device is still transitioning to the new state
+ // (i.e. about to become awake or enter doze)
+ void onDozeChanged(boolean isDozing, boolean isAwake);
}
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 6300a12..a13eada 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -491,8 +491,12 @@
public String toString() {
StringBuilder sb = new StringBuilder("ProgramSelector(type=").append(mProgramType)
.append(", primary=").append(mPrimaryId);
- if (mSecondaryIds.length > 0) sb.append(", secondary=").append(mSecondaryIds);
- if (mVendorIds.length > 0) sb.append(", vendor=").append(mVendorIds);
+ if (mSecondaryIds.length > 0) {
+ sb.append(", secondary=").append(Arrays.toString(mSecondaryIds));
+ }
+ if (mVendorIds.length > 0) {
+ sb.append(", vendor=").append(Arrays.toString(mVendorIds));
+ }
sb.append(")");
return sb.toString();
}
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 8180caa..4334116 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -505,7 +505,8 @@
public int hashCode() {
return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
- mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
+ mIsCaptureSupported, Arrays.hashCode(mBands), mIsBgScanSupported,
+ mDabFrequencyTable, mVendorInfo);
}
@Override
@@ -525,7 +526,7 @@
if (mNumAudioSources != other.mNumAudioSources) return false;
if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
- if (!Objects.equals(mBands, other.mBands)) return false;
+ if (!Arrays.equals(mBands, other.mBands)) return false;
if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false;
if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 48b9b88..3a157d3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -107,6 +107,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.MotionEvent.ToolType;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
@@ -947,7 +948,7 @@
* @hide
*/
@Override
- public void updateEditorToolType(int toolType) {
+ public void updateEditorToolType(@ToolType int toolType) {
onUpdateEditorToolType(toolType);
}
@@ -3079,10 +3080,13 @@
* {@link MotionEvent#getToolType(int)} was used to click the editor.
* e.g. when toolType is {@link MotionEvent#TOOL_TYPE_STYLUS}, IME may choose to show a
* companion widget instead of normal virtual keyboard.
+ * <p> This method is called after {@link #onStartInput(EditorInfo, boolean)} and before
+ * {@link #onStartInputView(EditorInfo, boolean)} when editor was clicked with a known tool
+ * type.</p>
* <p> Default implementation does nothing. </p>
* @param toolType what {@link MotionEvent#getToolType(int)} was used to click on editor.
*/
- public void onUpdateEditorToolType(int toolType) {
+ public void onUpdateEditorToolType(@ToolType int toolType) {
// Intentionally empty
}
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index a19eb56..8644d91 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -23,6 +23,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import java.util.Arrays;
/**
@@ -395,7 +396,7 @@
out[i * 2] = entries.keyAt(i);
out[i * 2 + 1] = entries.valueAt(i);
}
- int size = out.toString().getBytes().length;
+ int size = Arrays.toString(out).getBytes().length;
if (size > MAX_SERIALIZED_SIZE) {
Log.i(TAG, "Log line too long, did not emit: " + size + " bytes.");
throw new RuntimeException();
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 43a6be5..fb8f84a 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -469,7 +469,15 @@
* 2) The per-application switch (i.e. Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS and
* Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES; which corresponds to the
* “angle_gl_driver_selection_pkgs” and “angle_gl_driver_selection_values” settings); if it
- * forces a choice; otherwise ...
+ * forces a choice;
+ * - Workaround Note: ANGLE and Vulkan currently have issues with applications that use YUV
+ * target functionality. The ANGLE broadcast receiver code will apply a "deferlist" at
+ * the first boot of a newly-flashed device. However, there is a gap in time between
+ * when applications can start and when the deferlist is applied. For now, assume that
+ * if ANGLE is the system driver and Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS is
+ * empty, that the deferlist has not yet been applied. In this case, select the Legacy
+ * driver.
+ * otherwise ...
* 3) Use ANGLE if isAngleEnabledByGameMode() returns true; otherwise ...
* 4) The global switch (i.e. use the system driver, whether ANGLE or legacy;
* a.k.a. mAngleIsSystemDriver, which is set by the device’s “ro.hardware.egl” property)
@@ -509,9 +517,16 @@
final List<String> optInValues = getGlobalSettingsString(
contentResolver, bundle, Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES);
Log.v(TAG, "Currently set values for:");
- Log.v(TAG, " angle_gl_driver_selection_pkgs = " + optInPackages);
+ Log.v(TAG, " angle_gl_driver_selection_pkgs =" + optInPackages);
Log.v(TAG, " angle_gl_driver_selection_values =" + optInValues);
+ // If ANGLE is the system driver AND the deferlist has not yet been applied, select the
+ // Legacy driver
+ if (mAngleIsSystemDriver && optInPackages.size() <= 1) {
+ Log.v(TAG, "Ignoring angle_gl_driver_selection_* until deferlist has been applied");
+ return ANGLE_GL_DRIVER_TO_USE_LEGACY;
+ }
+
// Make sure we have good settings to use
if (optInPackages.size() != optInValues.size()) {
Log.w(TAG,
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 412a33a..113a640 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -26,6 +26,9 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.IActivityManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -344,6 +347,13 @@
public static final int NETWORK_POLICY_LOG = 1;
/** {@hide} */
public static final int NETWORK_POLICY_REJECT = 2;
+
+ /**
+ * Detect explicit calls to {@link Runtime#gc()}.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ static final long DETECT_EXPLICIT_GC = 3400644L;
// TODO: wrap in some ImmutableHashMap thing.
// Note: must be before static initialization of sVmPolicy.
@@ -496,6 +506,7 @@
* <p>As of the Gingerbread release this includes network and disk operations but will
* likely expand in future releases.
*/
+ @SuppressWarnings("AndroidFrameworkCompatChange")
public @NonNull Builder detectAll() {
detectDiskReads();
detectDiskWrites();
@@ -511,6 +522,9 @@
if (targetSdk >= Build.VERSION_CODES.O) {
detectUnbufferedIo();
}
+ if (CompatChanges.isChangeEnabled(DETECT_EXPLICIT_GC)) {
+ detectExplicitGc();
+ }
return this;
}
@@ -591,26 +605,16 @@
}
/**
- * Detect explicit GC requests, i.e. calls to Runtime.gc().
- *
- * @hide
+ * Detect calls to {@link Runtime#gc()}.
*/
- @TestApi
public @NonNull Builder detectExplicitGc() {
- // TODO(b/3400644): Un-hide this for next API update
- // TODO(b/3400644): Un-hide ExplicitGcViolation for next API update
- // TODO(b/3400644): Make DETECT_EXPLICIT_GC a @TestApi for next API update
- // TODO(b/3400644): Call this from detectAll in next API update
return enable(DETECT_THREAD_EXPLICIT_GC);
}
/**
- * Disable detection of explicit GC requests, i.e. calls to Runtime.gc().
- *
- * @hide
+ * Disable detection of calls to {@link Runtime#gc()}.
*/
public @NonNull Builder permitExplicitGc() {
- // TODO(b/3400644): Un-hide this for next API update
return disable(DETECT_THREAD_EXPLICIT_GC);
}
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 06930bb..392bf4e 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -145,7 +145,8 @@
*/
@IntDef(prefix = { "FLAG_" }, flag = true, value = {
FLAG_BYPASS_INTERRUPTION_POLICY,
- FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
+ FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
+ FLAG_INVALIDATE_SETTINGS_CACHE
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flag{}
@@ -182,7 +183,7 @@
*
* @hide
*/
- public static final int FLAG_INVALIDATE_SETTINGS_CACHE = 0x3;
+ public static final int FLAG_INVALIDATE_SETTINGS_CACHE = 0x4;
/**
* All flags supported by vibrator service, update it when adding new flag.
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 13b22d3..a1ed253 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -26,6 +26,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;
@@ -515,7 +516,7 @@
throw new IOException("Unsupported log2BlockSize: " + hashingInfo.log2BlockSize);
}
if (hashingInfo.salt != null && hashingInfo.salt.length > 0) {
- throw new IOException("Unsupported salt: " + hashingInfo.salt);
+ throw new IOException("Unsupported salt: " + Arrays.toString(hashingInfo.salt));
}
if (hashingInfo.rawRootHash.length != INCFS_MAX_HASH_SIZE) {
throw new IOException("rawRootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
diff --git a/core/java/android/os/strictmode/ExplicitGcViolation.java b/core/java/android/os/strictmode/ExplicitGcViolation.java
index 583ed1a..c4ae82d 100644
--- a/core/java/android/os/strictmode/ExplicitGcViolation.java
+++ b/core/java/android/os/strictmode/ExplicitGcViolation.java
@@ -19,10 +19,7 @@
/**
* See #{@link android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc()}.
- *
- * @hide
*/
-@TestApi
public final class ExplicitGcViolation extends Violation {
/** @hide */
public ExplicitGcViolation() {
diff --git a/core/java/android/service/autofill/OptionalValidators.java b/core/java/android/service/autofill/OptionalValidators.java
index 7189c88..2043539 100644
--- a/core/java/android/service/autofill/OptionalValidators.java
+++ b/core/java/android/service/autofill/OptionalValidators.java
@@ -25,6 +25,8 @@
import com.android.internal.util.Preconditions;
+import java.util.Arrays;
+
/**
* Compound validator that returns {@code true} on {@link #isValid(ValueFinder)} if any
* of its subvalidators returns {@code true} as well.
@@ -61,7 +63,8 @@
public String toString() {
if (!sDebug) return super.toString();
- return new StringBuilder("OptionalValidators: [validators=").append(mValidators)
+ return new StringBuilder("OptionalValidators: [validators=")
+ .append(Arrays.toString(mValidators))
.append("]")
.toString();
}
diff --git a/core/java/android/service/autofill/RequiredValidators.java b/core/java/android/service/autofill/RequiredValidators.java
index 619eba0..054582e 100644
--- a/core/java/android/service/autofill/RequiredValidators.java
+++ b/core/java/android/service/autofill/RequiredValidators.java
@@ -25,6 +25,8 @@
import com.android.internal.util.Preconditions;
+import java.util.Arrays;
+
/**
* Compound validator that only returns {@code true} on {@link #isValid(ValueFinder)} if all
* of its subvalidators return {@code true} as well.
@@ -60,7 +62,8 @@
public String toString() {
if (!sDebug) return super.toString();
- return new StringBuilder("RequiredValidators: [validators=").append(mValidators)
+ return new StringBuilder("RequiredValidators: [validators=")
+ .append(Arrays.toString(mValidators))
.append("]")
.toString();
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index d08a67e..f0f16db 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -75,6 +75,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -599,7 +600,7 @@
VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
try {
if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface
- + " finished=" + finished + " selections=" + selections
+ + " finished=" + finished + " selections=" + Arrays.toString(selections)
+ " result=" + result);
if (finished) {
finishRequest();
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index 3bde32b..3177c5c 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -24,6 +24,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
+import java.util.Arrays;
/**
* Speech synthesis request that writes the audio to a WAV file.
@@ -152,8 +153,8 @@
@Override
public int audioAvailable(byte[] buffer, int offset, int length) {
if (DBG) {
- Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + buffer + "," + offset
- + "," + length + ")");
+ Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + Arrays.toString(buffer)
+ + "," + offset + "," + length + ")");
}
FileChannel fileChannel = null;
synchronized (mStateLock) {
diff --git a/core/java/android/text/GraphemeClusterSegmentIterator.java b/core/java/android/text/GraphemeClusterSegmentIterator.java
new file mode 100644
index 0000000..e3976a7
--- /dev/null
+++ b/core/java/android/text/GraphemeClusterSegmentIterator.java
@@ -0,0 +1,78 @@
+/*
+ * 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.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+
+/**
+ * Implementation of {@code SegmentIterator} using grapheme clusters as the text segment. Whitespace
+ * characters are included as segments.
+ *
+ * @hide
+ */
+public class GraphemeClusterSegmentIterator extends SegmentIterator {
+ private final CharSequence mText;
+ private final TextPaint mTextPaint;
+
+ public GraphemeClusterSegmentIterator(
+ @NonNull CharSequence text, @NonNull TextPaint textPaint) {
+ mText = text;
+ mTextPaint = textPaint;
+ }
+
+ @Override
+ public int previousStartBoundary(@IntRange(from = 0) int offset) {
+ int boundary = mTextPaint.getTextRunCursor(
+ mText, 0, mText.length(), false, offset, Paint.CURSOR_BEFORE);
+ return boundary == -1 ? DONE : boundary;
+ }
+
+ @Override
+ public int previousEndBoundary(@IntRange(from = 0) int offset) {
+ int boundary = mTextPaint.getTextRunCursor(
+ mText, 0, mText.length(), false, offset, Paint.CURSOR_BEFORE);
+ // Check that there is another cursor position before, otherwise this is not a valid
+ // end boundary.
+ if (mTextPaint.getTextRunCursor(
+ mText, 0, mText.length(), false, boundary, Paint.CURSOR_BEFORE) == -1) {
+ return DONE;
+ }
+ return boundary == -1 ? DONE : boundary;
+ }
+
+ @Override
+ public int nextStartBoundary(@IntRange(from = 0) int offset) {
+ int boundary = mTextPaint.getTextRunCursor(
+ mText, 0, mText.length(), false, offset, Paint.CURSOR_AFTER);
+ // Check that there is another cursor position after, otherwise this is not a valid
+ // start boundary.
+ if (mTextPaint.getTextRunCursor(
+ mText, 0, mText.length(), false, boundary, Paint.CURSOR_AFTER) == -1) {
+ return DONE;
+ }
+ return boundary == -1 ? DONE : boundary;
+ }
+
+ @Override
+ public int nextEndBoundary(@IntRange(from = 0) int offset) {
+ int boundary = mTextPaint.getTextRunCursor(
+ mText, 0, mText.length(), false, offset, Paint.CURSOR_AFTER);
+ return boundary == -1 ? DONE : boundary;
+ }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 4efc838..b5f7c54 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -19,11 +19,13 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.text.LineBreaker;
import android.os.Build;
import android.text.method.TextKeyListener;
@@ -710,8 +712,7 @@
}
/**
- * Return the start position of the line, given the left and right bounds
- * of the margins.
+ * Return the start position of the line, given the left and right bounds of the margins.
*
* @param line the line index
* @param left the left bounds (0, or leading margin if ltr para)
@@ -1312,6 +1313,38 @@
return horizontal;
}
+ private void fillHorizontalBoundsForLine(int line, float[] horizontalBounds) {
+ final int lineStart = getLineStart(line);
+ final int lineEnd = getLineEnd(line);
+ final int lineLength = lineEnd - lineStart;
+
+ final int dir = getParagraphDirection(line);
+ final Directions directions = getLineDirections(line);
+
+ final boolean hasTab = getLineContainsTab(line);
+ TabStops tabStops = null;
+ if (hasTab && mText instanceof Spanned) {
+ // Just checking this line should be good enough, tabs should be
+ // consistent across all lines in a paragraph.
+ TabStopSpan[] tabs =
+ getParagraphSpans((Spanned) mText, lineStart, lineEnd, TabStopSpan.class);
+ if (tabs.length > 0) {
+ tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
+ }
+ }
+
+ final TextLine tl = TextLine.obtain();
+ tl.set(mPaint, mText, lineStart, lineEnd, dir, directions, hasTab, tabStops,
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
+ if (horizontalBounds == null || horizontalBounds.length < 2 * lineLength) {
+ horizontalBounds = new float[2 * lineLength];
+ }
+
+ tl.measureAllBounds(horizontalBounds, null);
+ TextLine.recycle(tl);
+ }
+
/**
* Return the characters' bounds in the given range. The {@code bounds} array will be filled
* starting from {@code boundsStart} (inclusive). The coordinates are in local text layout.
@@ -1358,32 +1391,11 @@
final int lineStart = getLineStart(line);
final int lineEnd = getLineEnd(line);
final int lineLength = lineEnd - lineStart;
-
- final int dir = getParagraphDirection(line);
- final boolean hasTab = getLineContainsTab(line);
- final Directions directions = getLineDirections(line);
-
- TabStops tabStops = null;
- if (hasTab && mText instanceof Spanned) {
- // Just checking this line should be good enough, tabs should be
- // consistent across all lines in a paragraph.
- TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, lineStart, lineEnd,
- TabStopSpan.class);
- if (tabs.length > 0) {
- tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
- }
- }
-
- final TextLine tl = TextLine.obtain();
- tl.set(mPaint, mText, lineStart, lineEnd, dir, directions, hasTab, tabStops,
- getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
- isFallbackLineSpacingEnabled());
if (horizontalBounds == null || horizontalBounds.length < 2 * lineLength) {
horizontalBounds = new float[2 * lineLength];
}
+ fillHorizontalBoundsForLine(line, horizontalBounds);
- tl.measureAllBounds(horizontalBounds, null);
- TextLine.recycle(tl);
final int lineLeft = getParagraphLeft(line);
final int lineRight = getParagraphRight(line);
final int lineStartPos = getLineStartPos(line, lineLeft, lineRight);
@@ -1802,6 +1814,380 @@
}
/**
+ * Finds the range of text which is inside the specified rectangle area. The start of the range
+ * is the start of the first text segment inside the area, and the end of the range is the end
+ * of the last text segment inside the area.
+ *
+ * <p>A text segment is considered to be inside the area if the center of its bounds is inside
+ * the area. If a text segment spans multiple lines or multiple directional runs (e.g. a
+ * hyphenated word), the text segment is divided into pieces at the line and run breaks, then
+ * the text segment is considered to be inside the area if any of its pieces have their center
+ * inside the area.
+ *
+ * <p>The returned range may also include text segments which are not inside the specified area,
+ * if those text segments are in between text segments which are inside the area. For example,
+ * the returned range may be "segment1 segment2 segment3" if "segment1" and "segment3" are
+ * inside the area and "segment2" is not.
+ *
+ * @param area area for which the text range will be found
+ * @param segmentIterator iterator for determining the ranges of text to be considered as a text
+ * segment
+ * @return int array of size 2 containing the start (inclusive) and end (exclusive) character
+ * offsets of the range, or null if there are no text segments inside the area
+ * @hide
+ */
+ @Nullable
+ public int[] getRangeForRect(@NonNull RectF area, @NonNull SegmentIterator segmentIterator) {
+ // Find the first line whose vertical center is below the top of the area.
+ int startLine = getLineForVertical((int) area.top);
+ int startLineTop = getLineTop(startLine);
+ int startLineBottom = getLineBottomWithoutSpacing(startLine);
+ if (area.top > (startLineTop + startLineBottom) / 2f) {
+ startLine++;
+ if (startLine >= getLineCount()) {
+ // The top of the area is below the vertical center of the last line, so the area
+ // does not contain any text.
+ return null;
+ }
+ }
+
+ // Find the last line whose vertical center is above the bottom of the area.
+ int endLine = getLineForVertical((int) area.bottom);
+ int endLineTop = getLineTop(endLine);
+ int endLineBottom = getLineBottomWithoutSpacing(endLine);
+ if (area.bottom < (endLineTop + endLineBottom) / 2f) {
+ endLine--;
+ }
+ if (endLine < startLine) {
+ // There are no lines with vertical centers between the top and bottom of the area, so
+ // the area does not contain any text.
+ return null;
+ }
+
+ int start = getStartOrEndOffsetForHorizontalInterval(
+ startLine, area.left, area.right, segmentIterator, /* getStart= */ true);
+ // If the area does not contain any text on this line, keep trying subsequent lines until
+ // the end line is reached.
+ while (start == -1 && startLine < endLine) {
+ startLine++;
+ start = getStartOrEndOffsetForHorizontalInterval(
+ startLine, area.left, area.right, segmentIterator, /* getStart= */ true);
+ }
+ if (start == -1) {
+ // All lines were checked, the area does not contain any text.
+ return null;
+ }
+
+ int end = getStartOrEndOffsetForHorizontalInterval(
+ endLine, area.left, area.right, segmentIterator, /* getStart= */ false);
+ // If the area does not contain any text on this line, keep trying previous lines until
+ // the start line is reached.
+ while (end == -1 && startLine < endLine) {
+ endLine--;
+ end = getStartOrEndOffsetForHorizontalInterval(
+ endLine, area.left, area.right, segmentIterator, /* getStart= */ false);
+ }
+ if (end == -1) {
+ // All lines were checked, the area does not contain any text.
+ return null;
+ }
+
+ // If a text segment spans multiple lines or multiple directional runs (e.g. a hyphenated
+ // word), then getStartOrEndOffsetForHorizontalInterval() can return an offset in the middle
+ // of a text segment. Adjust the range to include the rest of any partial text segments. If
+ // start is already the start boundary of a text segment, then this is a no-op.
+ start = segmentIterator.previousStartBoundary(start + 1);
+ end = segmentIterator.nextEndBoundary(end - 1);
+
+ return new int[] {start, end};
+ }
+
+ /**
+ * Finds the start character offset of the first text segment inside a horizontal interval
+ * within a line, or the end character offset of the last text segment inside the horizontal
+ * interval.
+ *
+ * @param line index of the line to search
+ * @param left left bound of the horizontal interval
+ * @param right right bound of the horizontal interval
+ * @param segmentIterator iterator for determining the ranges of text to be considered as a text
+ * segment
+ * @param getStart true to find the start of the first text segment inside the horizontal
+ * interval, false to find the end of the last text segment
+ * @return the start character offset of the first text segment inside the horizontal interval,
+ * or the end character offset of the last text segment inside the horizontal interval.
+ */
+ private int getStartOrEndOffsetForHorizontalInterval(
+ @IntRange(from = 0) int line, float left, float right,
+ @NonNull SegmentIterator segmentIterator, boolean getStart) {
+ int lineStartOffset = getLineStart(line);
+ int lineEndOffset = getLineEnd(line);
+ if (lineStartOffset == lineEndOffset) {
+ return -1;
+ }
+
+ float[] horizontalBounds = new float[2 * (lineEndOffset - lineStartOffset)];
+ fillHorizontalBoundsForLine(line, horizontalBounds);
+
+ int lineStartPos = getLineStartPos(line, getParagraphLeft(line), getParagraphRight(line));
+
+ // Loop through the runs forwards or backwards depending on getStart value.
+ Layout.Directions directions = getLineDirections(line);
+ int runIndex = getStart ? 0 : directions.getRunCount() - 1;
+ while ((getStart && runIndex < directions.getRunCount()) || (!getStart && runIndex >= 0)) {
+ // runStartOffset and runEndOffset are offset indices within the line.
+ int runStartOffset = directions.getRunStart(runIndex);
+ int runEndOffset = Math.min(
+ runStartOffset + directions.getRunLength(runIndex),
+ lineEndOffset - lineStartOffset);
+ boolean isRtl = directions.isRunRtl(runIndex);
+ float runLeft = lineStartPos
+ + (isRtl
+ ? horizontalBounds[2 * (runEndOffset - 1)]
+ : horizontalBounds[2 * runStartOffset]);
+ float runRight = lineStartPos
+ + (isRtl
+ ? horizontalBounds[2 * runStartOffset + 1]
+ : horizontalBounds[2 * (runEndOffset - 1) + 1]);
+
+ int result =
+ getStart
+ ? getStartOffsetForHorizontalIntervalWithinRun(
+ left, right, lineStartOffset, lineStartPos, horizontalBounds,
+ runStartOffset, runEndOffset, runLeft, runRight, isRtl,
+ segmentIterator)
+ : getEndOffsetForHorizontalIntervalWithinRun(
+ left, right, lineStartOffset, lineStartPos, horizontalBounds,
+ runStartOffset, runEndOffset, runLeft, runRight, isRtl,
+ segmentIterator);
+ if (result >= 0) {
+ return result;
+ }
+
+ runIndex += getStart ? 1 : -1;
+ }
+ return -1;
+ }
+
+ /**
+ * Finds the start character offset of the first text segment inside a horizontal interval
+ * within a directional run.
+ *
+ * @param left left bound of the horizontal interval
+ * @param right right bound of the horizontal interval
+ * @param lineStartOffset start character offset of the line containing this run
+ * @param lineStartPos start position of the line containing this run
+ * @param horizontalBounds array containing the signed horizontal bounds of the characters in
+ * the line. The left and right bounds of the character at offset i are stored at index (2 *
+ * i) and index (2 * i + 1). Bounds are relative to {@code lineStartPos}.
+ * @param runStartOffset start offset of the run relative to {@code lineStartOffset}
+ * @param runEndOffset end offset of the run relative to {@code lineStartOffset}
+ * @param runLeft left bound of the run
+ * @param runRight right bound of the run
+ * @param isRtl whether the run is right-to-left
+ * @param segmentIterator iterator for determining the ranges of text to be considered as a text
+ * segment
+ * @return the start character offset of the first text segment inside the horizontal interval
+ */
+ private static int getStartOffsetForHorizontalIntervalWithinRun(
+ float left, float right,
+ @IntRange(from = 0) int lineStartOffset,
+ @IntRange(from = 0) int lineStartPos,
+ @NonNull float[] horizontalBounds,
+ @IntRange(from = 0) int runStartOffset, @IntRange(from = 0) int runEndOffset,
+ float runLeft, float runRight,
+ boolean isRtl,
+ @NonNull SegmentIterator segmentIterator) {
+ if (runRight < left || runLeft > right) {
+ // The run does not overlap the interval.
+ return -1;
+ }
+
+ // Find the first character in the run whose bounds overlap with the interval.
+ // firstCharOffset is an offset index within the line.
+ int firstCharOffset;
+ if ((!isRtl && left <= runLeft) || (isRtl && right >= runRight)) {
+ firstCharOffset = runStartOffset;
+ } else {
+ int low = runStartOffset;
+ int high = runEndOffset;
+ int guess;
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+ // Left edge of the character at guess
+ float pos = lineStartPos + horizontalBounds[2 * guess];
+ if ((!isRtl && pos > left) || (isRtl && pos < right)) {
+ high = guess;
+ } else {
+ low = guess;
+ }
+ }
+ // The interval bound is between the left edge of the character at low and the left edge
+ // of the character at high. For LTR text, this is within the character at low. For RTL
+ // text, this is within the character at high.
+ firstCharOffset = isRtl ? high : low;
+ }
+
+ // Find the first text segment containing this character (or, if no text segment contains
+ // this character, the first text segment after this character). All previous text segments
+ // in this run are to the left (for LTR) of the interval.
+ segmentIterator.setRunLimits(
+ lineStartOffset + runStartOffset, lineStartOffset + runEndOffset);
+ int segmentEndOffset =
+ segmentIterator.nextEndBoundaryOrRunEnd(lineStartOffset + firstCharOffset);
+ if (segmentEndOffset == SegmentIterator.DONE) {
+ // There are no text segments containing or after firstCharOffset, so no text segments
+ // in this run overlap the interval.
+ return -1;
+ }
+ int segmentStartOffset = segmentIterator.previousStartBoundaryOrRunStart(segmentEndOffset);
+ float segmentCenter = lineStartPos
+ + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)]
+ + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2;
+ if ((!isRtl && segmentCenter > right) || (isRtl && segmentCenter < left)) {
+ // The entire interval is to the left (for LTR) of the text segment's center. So the
+ // interval does not contain any text segments within this run.
+ return -1;
+ }
+ if ((!isRtl && segmentCenter >= left) || (isRtl && segmentCenter <= right)) {
+ // The center is within the interval, so return the start offset of this text segment.
+ return segmentStartOffset;
+ }
+
+ // If first text segment's center is not within the interval, try the next text segment.
+ segmentStartOffset = segmentIterator.nextStartBoundaryWithinRunLimits(segmentStartOffset);
+ if (segmentStartOffset == SegmentIterator.DONE
+ || segmentStartOffset == lineStartOffset + runEndOffset) {
+ // No more text segments within this run.
+ return -1;
+ }
+ segmentEndOffset = segmentIterator.nextEndBoundaryOrRunEnd(segmentStartOffset);
+ segmentCenter = lineStartPos
+ + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)]
+ + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2;
+ // We already know that segmentCenter >= left (for LTR) since the previous word contains the
+ // left point.
+ if ((!isRtl && segmentCenter <= right) || (isRtl && segmentCenter >= left)) {
+ return segmentStartOffset;
+ }
+
+ // If the second text segment is also not within the interval, then this means that the
+ // interval is between the centers of the first and second text segments, so it does not
+ // contain any text segments on this line.
+ return -1;
+ }
+
+ /**
+ * Finds the end character offset of the last text segment inside a horizontal interval within a
+ * directional run.
+ *
+ * @param left left bound of the horizontal interval
+ * @param right right bound of the horizontal interval
+ * @param lineStartOffset start character offset of the line containing this run
+ * @param lineStartPos start position of the line containing this run
+ * @param horizontalBounds array containing the signed horizontal bounds of the characters in
+ * the line. The left and right bounds of the character at offset i are stored at index (2 *
+ * i) and index (2 * i + 1). Bounds are relative to {@code lineStartPos}.
+ * @param runStartOffset start offset of the run relative to {@code lineStartOffset}
+ * @param runEndOffset end offset of the run relative to {@code lineStartOffset}
+ * @param runLeft left bound of the run
+ * @param runRight right bound of the run
+ * @param isRtl whether the run is right-to-left
+ * @param segmentIterator iterator for determining the ranges of text to be considered as a text
+ * segment
+ * @return the end character offset of the last text segment inside the horizontal interval
+ */
+ private static int getEndOffsetForHorizontalIntervalWithinRun(
+ float left, float right,
+ @IntRange(from = 0) int lineStartOffset,
+ @IntRange(from = 0) int lineStartPos,
+ @NonNull float[] horizontalBounds,
+ @IntRange(from = 0) int runStartOffset, @IntRange(from = 0) int runEndOffset,
+ float runLeft, float runRight,
+ boolean isRtl,
+ @NonNull SegmentIterator segmentIterator) {
+ if (runRight < left || runLeft > right) {
+ // The run does not overlap the interval.
+ return -1;
+ }
+
+ // Find the last character in the run whose bounds overlap with the interval.
+ // firstCharOffset is an offset index within the line.
+ int lastCharOffset;
+ if ((!isRtl && right >= runRight) || (isRtl && left <= runLeft)) {
+ lastCharOffset = runEndOffset - 1;
+ } else {
+ int low = runStartOffset;
+ int high = runEndOffset;
+ int guess;
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+ // Left edge of the character at guess
+ float pos = lineStartPos + horizontalBounds[2 * guess];
+ if ((!isRtl && pos > right) || (isRtl && pos < left)) {
+ high = guess;
+ } else {
+ low = guess;
+ }
+ }
+ // The interval bound is between the left edge of the character at low and the left edge
+ // of the character at high. For LTR text, this is within the character at low. For RTL
+ // text, this is within the character at high.
+ lastCharOffset = isRtl ? high : low;
+ }
+
+ // Find the last text segment containing this character (or, if no text segment
+ // contains this character, the first text segment before this character). All
+ // following text segments in this run are to the right (for LTR) of the interval.
+ segmentIterator.setRunLimits(
+ lineStartOffset + runStartOffset, lineStartOffset + runEndOffset);
+ // + 1 to allow segmentStartOffset = lineStartOffset + lastCharOffset
+ int segmentStartOffset =
+ segmentIterator.previousStartBoundaryOrRunStart(
+ lineStartOffset + lastCharOffset + 1);
+ if (segmentStartOffset == SegmentIterator.DONE) {
+ // There are no text segments containing or before lastCharOffset, so no text segments
+ // in this run overlap the interval.
+ return -1;
+ }
+ int segmentEndOffset = segmentIterator.nextEndBoundaryOrRunEnd(segmentStartOffset);
+ float segmentCenter = lineStartPos
+ + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)]
+ + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2;
+ if ((!isRtl && segmentCenter < left) || (isRtl && segmentCenter > right)) {
+ // The entire interval is to the right (for LTR) of the text segment's center. So the
+ // interval does not contain any text segments within this run.
+ return -1;
+ }
+ if ((!isRtl && segmentCenter <= right) || (isRtl && segmentCenter >= left)) {
+ // The center is within the interval, so return the end offset of this text segment.
+ return segmentEndOffset;
+ }
+
+ // If first text segment's center is not within the interval, try the next text segment.
+ segmentEndOffset = segmentIterator.previousEndBoundaryWithinRunLimits(segmentEndOffset);
+ if (segmentEndOffset == SegmentIterator.DONE
+ || segmentEndOffset == lineStartOffset + runStartOffset) {
+ // No more text segments within this run.
+ return -1;
+ }
+ segmentStartOffset = segmentIterator.previousStartBoundaryOrRunStart(segmentEndOffset);
+ segmentCenter = lineStartPos
+ + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)]
+ + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2;
+ // We already know that segmentCenter <= right (for LTR) since the following word
+ // contains the right point.
+ if ((!isRtl && segmentCenter >= left) || (isRtl && segmentCenter <= right)) {
+ return segmentEndOffset;
+ }
+
+ // If the second text segment is also not within the interval, then this means that the
+ // interval is between the centers of the first and second text segments, so it does not
+ // contain any text segments on this line.
+ return -1;
+ }
+
+ /**
* Return the text offset after the last character on the specified line.
*/
public final int getLineEnd(int line) {
diff --git a/core/java/android/text/SegmentIterator.java b/core/java/android/text/SegmentIterator.java
new file mode 100644
index 0000000..00522e5
--- /dev/null
+++ b/core/java/android/text/SegmentIterator.java
@@ -0,0 +1,111 @@
+/*
+ * 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.text;
+
+import android.annotation.IntRange;
+
+/**
+ * Finds text segment boundaries within text. Subclasses can implement different types of text
+ * segments. Grapheme clusters and words are examples of possible text segments.
+ *
+ * <p>Granular units may not overlap, so every character belongs to at most one text segment. A
+ * character may belong to no text segments.
+ *
+ * <p>For example, a word level text segment iterator may subdivide the text "Hello, World!" into
+ * four text segments: "Hello", ",", "World", "!". The space character does not belong to any text
+ * segments.
+ *
+ * @hide
+ */
+public abstract class SegmentIterator {
+ public static final int DONE = -1;
+
+ private int mRunStartOffset;
+ private int mRunEndOffset;
+
+ /**
+ * Returns the character offset of the previous text segment start boundary before the specified
+ * character offset, or {@code DONE} if there are none.
+ */
+ public abstract int previousStartBoundary(@IntRange(from = 0) int offset);
+
+ /**
+ * Returns the character offset of the previous text segment end boundary before the specified
+ * character offset, or {@code DONE} if there are none.
+ */
+ public abstract int previousEndBoundary(@IntRange(from = 0) int offset);
+
+ /**
+ * Returns the character offset of the next text segment start boundary after the specified
+ * character offset, or {@code DONE} if there are none.
+ */
+ public abstract int nextStartBoundary(@IntRange(from = 0) int offset);
+
+ /**
+ * Returns the character offset of the next text segment end boundary after the specified
+ * character offset, or {@code DONE} if there are none.
+ */
+ public abstract int nextEndBoundary(@IntRange(from = 0) int offset);
+
+ /**
+ * Sets the start and end of a run which can be used to constrain the scope of the iterator's
+ * search.
+ *
+ * @hide
+ */
+ void setRunLimits(
+ @IntRange(from = 0) int runStartOffset, @IntRange(from = 0) int runEndOffset) {
+ mRunStartOffset = runStartOffset;
+ mRunEndOffset = runEndOffset;
+ }
+
+ /** @hide */
+ int previousStartBoundaryOrRunStart(@IntRange(from = 0) int offset) {
+ int start = previousStartBoundary(offset);
+ if (start == DONE) {
+ return DONE;
+ }
+ return Math.max(start, mRunStartOffset);
+ }
+
+ /** @hide */
+ int previousEndBoundaryWithinRunLimits(@IntRange(from = 0) int offset) {
+ int end = previousEndBoundary(offset);
+ if (end <= mRunStartOffset) {
+ return DONE;
+ }
+ return end;
+ }
+
+ /** @hide */
+ int nextStartBoundaryWithinRunLimits(@IntRange(from = 0) int offset) {
+ int start = nextStartBoundary(offset);
+ if (start >= mRunEndOffset) {
+ return DONE;
+ }
+ return start;
+ }
+
+ /** @hide */
+ int nextEndBoundaryOrRunEnd(@IntRange(from = 0) int offset) {
+ int end = nextEndBoundary(offset);
+ if (end == DONE) {
+ return DONE;
+ }
+ return Math.min(end, mRunEndOffset);
+ }
+}
diff --git a/core/java/android/text/WordSegmentIterator.java b/core/java/android/text/WordSegmentIterator.java
new file mode 100644
index 0000000..1115319
--- /dev/null
+++ b/core/java/android/text/WordSegmentIterator.java
@@ -0,0 +1,87 @@
+/*
+ * 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.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.icu.text.BreakIterator;
+import android.text.method.WordIterator;
+
+/**
+ * Implementation of {@code SegmentIterator} using words as the text segment. Word boundaries are
+ * found using {@code WordIterator}. Whitespace characters are excluded, so they are not included in
+ * any text segments.
+ *
+ * @hide
+ */
+public class WordSegmentIterator extends SegmentIterator {
+ private final CharSequence mText;
+ private final WordIterator mWordIterator;
+
+ public WordSegmentIterator(@NonNull CharSequence text, @NonNull WordIterator wordIterator) {
+ mText = text;
+ mWordIterator = wordIterator;
+ }
+
+ @Override
+ public int previousStartBoundary(@IntRange(from = 0) int offset) {
+ int boundary = offset;
+ do {
+ boundary = mWordIterator.prevBoundary(boundary);
+ if (boundary == BreakIterator.DONE) {
+ return DONE;
+ }
+ } while (Character.isWhitespace(mText.charAt(boundary)));
+ return boundary;
+ }
+
+ @Override
+ public int previousEndBoundary(@IntRange(from = 0) int offset) {
+ int boundary = offset;
+ do {
+ boundary = mWordIterator.prevBoundary(boundary);
+ if (boundary == BreakIterator.DONE || boundary == 0) {
+ return DONE;
+ }
+ } while (Character.isWhitespace(mText.charAt(boundary - 1)));
+ return boundary;
+ }
+
+ @Override
+ public int nextStartBoundary(@IntRange(from = 0) int offset) {
+ int boundary = offset;
+ do {
+ boundary = mWordIterator.nextBoundary(boundary);
+ if (boundary == BreakIterator.DONE || boundary == mText.length()) {
+ return DONE;
+ }
+ } while (Character.isWhitespace(mText.charAt(boundary)));
+ return boundary;
+ }
+
+ @Override
+ public int nextEndBoundary(@IntRange(from = 0) int offset) {
+ int boundary = offset;
+ do {
+ boundary = mWordIterator.nextBoundary(boundary);
+ if (boundary == BreakIterator.DONE) {
+ return DONE;
+ }
+ } while (Character.isWhitespace(mText.charAt(boundary - 1)));
+ return boundary;
+ }
+}
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index c0bc991..155f508 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -23,6 +23,7 @@
import libcore.util.EmptyArray;
+import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Map;
@@ -231,7 +232,7 @@
array[0] = array[1] = null;
mTwiceBaseCacheSize--;
if (DEBUG) {
- Log.d(TAG, "Retrieving 2x cache " + mHashes
+ Log.d(TAG, "Retrieving 2x cache " + Arrays.toString(mHashes)
+ " now have " + mTwiceBaseCacheSize + " entries");
}
return;
@@ -258,7 +259,7 @@
array[0] = array[1] = null;
mBaseCacheSize--;
if (DEBUG) {
- Log.d(TAG, "Retrieving 1x cache " + mHashes
+ Log.d(TAG, "Retrieving 1x cache " + Arrays.toString(mHashes)
+ " now have " + mBaseCacheSize + " entries");
}
return;
@@ -295,8 +296,10 @@
}
mTwiceBaseCache = array;
mTwiceBaseCacheSize++;
- if (DEBUG) Log.d(TAG, "Storing 2x cache " + array
- + " now have " + mTwiceBaseCacheSize + " entries");
+ if (DEBUG) {
+ Log.d(TAG, "Storing 2x cache " + Arrays.toString(array)
+ + " now have " + mTwiceBaseCacheSize + " entries");
+ }
}
}
} else if (hashes.length == BASE_SIZE) {
@@ -309,8 +312,10 @@
}
mBaseCache = array;
mBaseCacheSize++;
- if (DEBUG) Log.d(TAG, "Storing 1x cache " + array
- + " now have " + mBaseCacheSize + " entries");
+ if (DEBUG) {
+ Log.d(TAG, "Storing 1x cache " + Arrays.toString(array)
+ + " now have " + mBaseCacheSize + " entries");
+ }
}
}
}
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index b5c75b9..73114e2 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -23,6 +23,7 @@
import libcore.util.EmptyArray;
import java.lang.reflect.Array;
+import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
@@ -194,8 +195,8 @@
array[0] = array[1] = null;
sTwiceBaseCacheSize--;
if (DEBUG) {
- Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have "
- + sTwiceBaseCacheSize + " entries");
+ Log.d(TAG, "Retrieving 2x cache " + Arrays.toString(mHashes)
+ + " now have " + sTwiceBaseCacheSize + " entries");
}
return;
}
@@ -221,8 +222,8 @@
array[0] = array[1] = null;
sBaseCacheSize--;
if (DEBUG) {
- Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have "
- + sBaseCacheSize + " entries");
+ Log.d(TAG, "Retrieving 1x cache " + Arrays.toString(mHashes)
+ + " now have " + sBaseCacheSize + " entries");
}
return;
}
@@ -259,8 +260,8 @@
sTwiceBaseCache = array;
sTwiceBaseCacheSize++;
if (DEBUG) {
- Log.d(TAG, "Storing 2x cache " + array + " now have " + sTwiceBaseCacheSize
- + " entries");
+ Log.d(TAG, "Storing 2x cache " + Arrays.toString(array) + " now have "
+ + sTwiceBaseCacheSize + " entries");
}
}
}
@@ -275,7 +276,7 @@
sBaseCache = array;
sBaseCacheSize++;
if (DEBUG) {
- Log.d(TAG, "Storing 1x cache " + array + " now have "
+ Log.d(TAG, "Storing 1x cache " + Arrays.toString(array) + " now have "
+ sBaseCacheSize + " entries");
}
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index bca100a..20f01ae 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -68,6 +68,12 @@
public static final String SETTINGS_APP_LOCALE_OPT_IN_ENABLED =
"settings_app_locale_opt_in_enabled";
+ /**
+ * Launch the Volume panel in SystemUI.
+ * @hide
+ */
+ public static final String SETTINGS_VOLUME_PANEL_IN_SYSTEMUI =
+ "settings_volume_panel_in_systemui";
/** @hide */
public static final String SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS =
@@ -110,6 +116,12 @@
*/
public static final String SETTINGS_NEW_KEYBOARD_UI = "settings_new_keyboard_ui";
+ /**
+ * Enable the new pages which is implemented with SPA.
+ * @hide
+ */
+ public static final String SETTINGS_ENABLE_SPA = "settings_enable_spa";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -134,6 +146,7 @@
DEFAULT_FLAGS.put("settings_search_always_expand", "true");
DEFAULT_FLAGS.put(SETTINGS_ALLOW_INTENT_REDIRECTION_FOR_CLONE_PROFILE, "false");
DEFAULT_FLAGS.put(SETTINGS_APP_LOCALE_OPT_IN_ENABLED, "true");
+ DEFAULT_FLAGS.put(SETTINGS_VOLUME_PANEL_IN_SYSTEMUI, "false");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
@@ -141,6 +154,7 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_CLEAR_CALLING, "false");
DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_SIMPLE_CURSOR, "false");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
index 9789b10..9a15cd5 100644
--- a/core/java/android/util/proto/ProtoInputStream.java
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -21,6 +21,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Objects;
/**
* Class to read to a protobuf stream.
@@ -968,26 +970,17 @@
public String dumpDebugData() {
StringBuilder sb = new StringBuilder();
- sb.append("\nmFieldNumber : 0x" + Integer.toHexString(mFieldNumber));
- sb.append("\nmWireType : 0x" + Integer.toHexString(mWireType));
- sb.append("\nmState : 0x" + Integer.toHexString(mState));
- sb.append("\nmDiscardedBytes : 0x" + Integer.toHexString(mDiscardedBytes));
- sb.append("\nmOffset : 0x" + Integer.toHexString(mOffset));
- sb.append("\nmExpectedObjectTokenStack : ");
- if (mExpectedObjectTokenStack == null) {
- sb.append("null");
- } else {
- sb.append(mExpectedObjectTokenStack);
- }
- sb.append("\nmDepth : 0x" + Integer.toHexString(mDepth));
- sb.append("\nmBuffer : ");
- if (mBuffer == null) {
- sb.append("null");
- } else {
- sb.append(mBuffer);
- }
- sb.append("\nmBufferSize : 0x" + Integer.toHexString(mBufferSize));
- sb.append("\nmEnd : 0x" + Integer.toHexString(mEnd));
+ sb.append("\nmFieldNumber : 0x").append(Integer.toHexString(mFieldNumber));
+ sb.append("\nmWireType : 0x").append(Integer.toHexString(mWireType));
+ sb.append("\nmState : 0x").append(Integer.toHexString(mState));
+ sb.append("\nmDiscardedBytes : 0x").append(Integer.toHexString(mDiscardedBytes));
+ sb.append("\nmOffset : 0x").append(Integer.toHexString(mOffset));
+ sb.append("\nmExpectedObjectTokenStack : ")
+ .append(Objects.toString(mExpectedObjectTokenStack));
+ sb.append("\nmDepth : 0x").append(Integer.toHexString(mDepth));
+ sb.append("\nmBuffer : ").append(Arrays.toString(mBuffer));
+ sb.append("\nmBufferSize : 0x").append(Integer.toHexString(mBufferSize));
+ sb.append("\nmEnd : 0x").append(Integer.toHexString(mEnd));
return sb.toString();
}
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 8464d2d..58d9913 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -20,6 +20,7 @@
import android.util.Duration;
import java.io.IOException;
+import java.util.Arrays;
/**
* This class contains a list of helper functions to write common proto in
@@ -91,27 +92,27 @@
final int wireType = proto.getWireType();
long fieldConstant;
- sb.append("Offset : 0x" + Integer.toHexString(proto.getOffset()));
- sb.append("\nField Number : 0x" + Integer.toHexString(proto.getFieldNumber()));
+ sb.append("Offset : 0x").append(Integer.toHexString(proto.getOffset()));
+ sb.append("\nField Number : 0x").append(Integer.toHexString(proto.getFieldNumber()));
sb.append("\nWire Type : ");
switch (wireType) {
case ProtoStream.WIRE_TYPE_VARINT:
- sb.append("varint");
fieldConstant = ProtoStream.makeFieldId(fieldNumber,
ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64);
- sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant)));
+ sb.append("varint\nField Value : 0x");
+ sb.append(Long.toHexString(proto.readLong(fieldConstant)));
break;
case ProtoStream.WIRE_TYPE_FIXED64:
- sb.append("fixed64");
fieldConstant = ProtoStream.makeFieldId(fieldNumber,
ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64);
- sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant)));
+ sb.append("fixed64\nField Value : 0x");
+ sb.append(Long.toHexString(proto.readLong(fieldConstant)));
break;
case ProtoStream.WIRE_TYPE_LENGTH_DELIMITED:
- sb.append("length delimited");
fieldConstant = ProtoStream.makeFieldId(fieldNumber,
ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES);
- sb.append("\nField Bytes : " + proto.readBytes(fieldConstant));
+ sb.append("length delimited\nField Bytes : ");
+ sb.append(Arrays.toString(proto.readBytes(fieldConstant)));
break;
case ProtoStream.WIRE_TYPE_START_GROUP:
sb.append("start group");
@@ -120,13 +121,13 @@
sb.append("end group");
break;
case ProtoStream.WIRE_TYPE_FIXED32:
- sb.append("fixed32");
fieldConstant = ProtoStream.makeFieldId(fieldNumber,
ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32);
- sb.append("\nField Value : 0x" + Integer.toHexString(proto.readInt(fieldConstant)));
+ sb.append("fixed32\nField Value : 0x");
+ sb.append(Integer.toHexString(proto.readInt(fieldConstant)));
break;
default:
- sb.append("unknown(" + proto.getWireType() + ")");
+ sb.append("unknown(").append(proto.getWireType()).append(")");
}
return sb.toString();
}
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 380c104..9e25a3e 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -116,8 +116,10 @@
* and be issued to the display subsystem.
* </p>
* <p>
- * Equal to the sum of the values of all other time-valued metric
- * identifiers.
+ * The total duration is the difference in time between when the frame
+ * began and when it ended. This value may not be exactly equal to the
+ * sum of the values of all other time-valued metric identifiers because
+ * some stages may happen concurrently.
* </p>
*/
public static final int TOTAL_DURATION = 8;
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 57ba7e9..f9bb880 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -653,7 +653,7 @@
break;
case MotionEvent.ACTION_MOVE:
- if (mInLongPress || mInContextClick) {
+ if ((mCurrentDownEvent == null) || mInLongPress || mInContextClick) {
break;
}
@@ -736,6 +736,9 @@
break;
case MotionEvent.ACTION_UP:
+ if (mCurrentDownEvent == null) {
+ break;
+ }
mStillDown = false;
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0bed342..ea00125 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1509,6 +1509,13 @@
*/
public static final int TOOL_TYPE_PALM = 5;
+ /** @hide */
+ @Retention(SOURCE)
+ @IntDef(prefix = { "TOOL_TYPE_" }, value = {
+ TOOL_TYPE_UNKNOWN, TOOL_TYPE_FINGER, TOOL_TYPE_STYLUS, TOOL_TYPE_MOUSE,
+ TOOL_TYPE_ERASER, TOOL_TYPE_PALM})
+ public @interface ToolType {};
+
// NOTE: If you add a new tool type here you must also add it to:
// native/include/android/input.h
@@ -2422,7 +2429,7 @@
* @see #TOOL_TYPE_STYLUS
* @see #TOOL_TYPE_MOUSE
*/
- public final int getToolType(int pointerIndex) {
+ public @ToolType int getToolType(int pointerIndex) {
return nativeGetToolType(mNativePtr, pointerIndex);
}
@@ -3868,7 +3875,7 @@
* @return The symbolic name of the specified tool type.
* @hide
*/
- public static String toolTypeToString(int toolType) {
+ public static String toolTypeToString(@ToolType int toolType) {
String symbolicName = TOOL_TYPE_SYMBOLIC_NAMES.get(toolType);
return symbolicName != null ? symbolicName : Integer.toString(toolType);
}
@@ -4361,7 +4368,7 @@
*
* @see MotionEvent#getToolType(int)
*/
- public int toolType;
+ public @ToolType int toolType;
/**
* Resets the pointer properties to their initial values.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e0f02d6..2a45fa5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1943,7 +1943,7 @@
@Override
public int hashCode() {
- return Objects.hash(supportedDisplayModes, activeDisplayModeId, activeDisplayModeId,
+ return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId,
activeColorMode, hdrCapabilities);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e8179cf..9620b09 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -9575,7 +9575,7 @@
/**
* Return the connection ID for the {@link AccessibilityInteractionController} of this instance.
- * @see AccessibilityNodeInfo#makeQueryableFromAppProcess(View)
+ * @see AccessibilityNodeInfo#enableQueryFromAppProcess(View)
*/
public int getDirectAccessibilityConnectionId() {
return mAccessibilityInteractionConnectionManager.ensureDirectConnection();
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6ae59bf..35ec408 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -84,7 +84,7 @@
* <p>
* Once an accessibility node info is delivered to an accessibility service it is
* made immutable and calling a state mutation method generates an error. See
- * {@link #makeQueryableFromAppProcess(View)} if you would like to inspect the
+ * {@link #enableQueryFromAppProcess(View)} if you would like to inspect the
* node tree from the app process for testing or debugging tools.
* </p>
* <p>
@@ -1175,7 +1175,7 @@
* @return The child node.
*
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
- * calling {@link #makeQueryableFromAppProcess(View)}.
+ * calling {@link #enableQueryFromAppProcess(View)}.
*/
public AccessibilityNodeInfo getChild(int index) {
return getChild(index, FLAG_PREFETCH_DESCENDANTS_HYBRID);
@@ -1190,7 +1190,7 @@
* @return The child node.
*
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
- * calling {@link #makeQueryableFromAppProcess(View)}.
+ * calling {@link #enableQueryFromAppProcess(View)}.
*
* @see AccessibilityNodeInfo#getParent(int) for a description of prefetching.
*/
@@ -1914,7 +1914,7 @@
* @return The parent.
*
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
- * calling {@link #makeQueryableFromAppProcess(View)}.
+ * calling {@link #enableQueryFromAppProcess(View)}.
*/
public AccessibilityNodeInfo getParent() {
enforceSealed();
@@ -1943,7 +1943,7 @@
* @return The parent.
*
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
- * calling {@link #makeQueryableFromAppProcess(View)}.
+ * calling {@link #enableQueryFromAppProcess(View)}.
*
* @see #FLAG_PREFETCH_ANCESTORS
* @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
@@ -3669,29 +3669,39 @@
* {@link AccessibilityNodeInfo} tree and perform accessibility actions on nodes.
*
* <p>
- * This is intended for short-lived inspections from testing or debugging tools in the app
- * process. After calling this method, all nodes linked to this node (children, ancestors, etc.)
- * are also queryable. Operations on this node tree will only succeed as long as the associated
- * view hierarchy remains attached to a window.
- * </p>
- *
- * <p>
- * Calling this method more than once on the same node is a no-op; if you wish to inspect a
- * different view hierarchy then create a new node from any view in that hierarchy and call this
- * method on that node.
- * </p>
- *
- * <p>
* Testing or debugging tools should create this {@link AccessibilityNodeInfo} node using
* {@link View#createAccessibilityNodeInfo()} or {@link AccessibilityNodeProvider} and call this
* method, then navigate and interact with the node tree by calling methods on the node.
+ * Calling this method more than once on the same node is a no-op. After calling this method,
+ * all nodes linked to this node (children, ancestors, etc.) are also queryable.
+ * </p>
+ *
+ * <p>
+ * Here "query" refers to the following node operations:
+ * <li>check properties of this node (example: {@link #isScrollable()})</li>
+ * <li>find and query children (example: {@link #getChild(int)})</li>
+ * <li>find and query the parent (example: {@link #getParent()})</li>
+ * <li>find focus (examples: {@link #findFocus(int)}, {@link #focusSearch(int)})</li>
+ * <li>find and query other nodes (example: {@link #findAccessibilityNodeInfosByText(String)},
+ * {@link #findAccessibilityNodeInfosByViewId(String)})</li>
+ * <li>perform actions (example: {@link #performAction(int)})</li>
+ * </p>
+ *
+ * <p>
+ * This is intended for short-lived inspections from testing or debugging tools in the app
+ * process, as operations on this node tree will only succeed as long as the associated
+ * view hierarchy remains attached to a window. Since {@link AccessibilityNodeInfo} objects can
+ * quickly become out of sync with their corresponding {@link View} objects there is
+ * intentionally no "disable" method: if you wish to inspect a changed or different view
+ * hierarchy then create a new node from any view in that hierarchy and call this method on that
+ * node.
* </p>
*
* @param view The view that generated this node, or any view in the same view-root hierarchy.
* @throws IllegalStateException If called from an {@link AccessibilityService}, or if provided
* a {@link View} that is not attached to a window.
*/
- public void makeQueryableFromAppProcess(@NonNull View view) {
+ public void enableQueryFromAppProcess(@NonNull View view) {
enforceNotSealed();
if (mConnectionId != UNDEFINED_CONNECTION_ID) {
return;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 5515548..36b0334 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -42,6 +42,7 @@
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
import android.view.MotionEvent;
+import android.view.MotionEvent.ToolType;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -639,8 +640,7 @@
* Initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)} that
* was used to focus this editor.
*/
- private int mInitialToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
-
+ private @ToolType int mInitialToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
/**
* Editors may use this method to provide initial input text to IMEs. As the surrounding text
@@ -1022,7 +1022,7 @@
* @see InputMethodService#onUpdateEditorToolType(int)
* @return toolType {@link MotionEvent#getToolType(int)}.
*/
- public int getInitialToolType() {
+ public @ToolType int getInitialToolType() {
return mInitialToolType;
}
@@ -1034,7 +1034,7 @@
* @see MotionEvent#getToolType(int)
* @see InputMethodService#onUpdateEditorToolType(int)
*/
- public void setInitialToolType(int toolType) {
+ public void setInitialToolType(@ToolType int toolType) {
mInitialToolType = toolType;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 6825f03..f93b8b7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -226,8 +226,6 @@
final UndoInputFilter mUndoInputFilter = new UndoInputFilter(this);
boolean mAllowUndo = true;
- private int mLastInputSource = InputDevice.SOURCE_UNKNOWN;
-
private final MetricsLogger mMetricsLogger = new MetricsLogger();
// Cursor Controllers.
@@ -1735,8 +1733,6 @@
public void onTouchEvent(MotionEvent event) {
final boolean filterOutEvent = shouldFilterOutTouchEvent(event);
- mLastInputSource = event.getSource();
-
mLastButtonState = event.getButtonState();
if (filterOutEvent) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -1789,7 +1785,7 @@
}
private void showFloatingToolbar() {
- if (mTextActionMode != null && showUIForTouchScreen()) {
+ if (mTextActionMode != null && mTextView.showUIForTouchScreen()) {
// Delay "show" so it doesn't interfere with click confirmations
// or double-clicks that could "dismiss" the floating toolbar.
int delay = ViewConfiguration.getDoubleTapTimeout();
@@ -1870,7 +1866,7 @@
? getSelectionController() : getInsertionController();
if (cursorController != null && !cursorController.isActive()
&& !cursorController.isCursorBeingModified()
- && showUIForTouchScreen()) {
+ && mTextView.showUIForTouchScreen()) {
cursorController.show();
}
}
@@ -2521,7 +2517,7 @@
return false;
}
- if (!showUIForTouchScreen()) {
+ if (!mTextView.showUIForTouchScreen()) {
return false;
}
@@ -2677,7 +2673,7 @@
mTextView.postDelayed(mShowSuggestionRunnable,
ViewConfiguration.getDoubleTapTimeout());
} else if (hasInsertionController()) {
- if (shouldInsertCursor && showUIForTouchScreen()) {
+ if (shouldInsertCursor && mTextView.showUIForTouchScreen()) {
getInsertionController().show();
} else {
getInsertionController().hide();
@@ -5408,7 +5404,7 @@
final boolean shouldShow = checkForTransforms() /*check not rotated and compute scale*/
&& !tooLargeTextForMagnifier()
&& obtainMagnifierShowCoordinates(event, showPosInView)
- && showUIForTouchScreen();
+ && mTextView.showUIForTouchScreen();
if (shouldShow) {
// Make the cursor visible and stop blinking.
mRenderCursorRegardlessTiming = true;
@@ -6354,16 +6350,6 @@
}
}
- /**
- * Returns true when need to show UIs, e.g. floating toolbar, etc, for finger based interaction.
- *
- * @return true if UIs need to show for finger interaciton. false if UIs are not necessary.
- */
- public boolean showUIForTouchScreen() {
- return (mLastInputSource & InputDevice.SOURCE_TOUCHSCREEN)
- == InputDevice.SOURCE_TOUCHSCREEN;
- }
-
/** Controller for the insertion cursor. */
@VisibleForTesting
public class InsertionPointCursorController implements CursorController {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 4ccd77b..be6b08f 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -301,7 +301,7 @@
final SelectionModifierCursorController controller = mEditor.getSelectionController();
if (controller != null
&& (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
- if (mEditor.showUIForTouchScreen()) {
+ if (mTextView.showUIForTouchScreen()) {
controller.show();
} else {
controller.hide();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6a572c5..f53dd0c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -72,6 +72,7 @@
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Path;
+import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -98,12 +99,14 @@
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.GetChars;
+import android.text.GraphemeClusterSegmentIterator;
import android.text.GraphicsOperations;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
import android.text.ParcelableSpan;
import android.text.PrecomputedText;
+import android.text.SegmentIterator;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
@@ -117,6 +120,7 @@
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.text.TextWatcher;
+import android.text.WordSegmentIterator;
import android.text.method.AllCapsTransformationMethod;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.DateKeyListener;
@@ -183,11 +187,15 @@
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.SelectGesture;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.view.inspector.InspectableProperty.FlagEntry;
@@ -965,6 +973,11 @@
private int mDeviceProvisionedState = DEVICE_PROVISIONED_UNKNOWN;
/**
+ * The last input source on this TextView.
+ */
+ private int mLastInputSource = InputDevice.SOURCE_UNKNOWN;
+
+ /**
* The TextView does not auto-size text (default).
*/
public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0;
@@ -9283,6 +9296,83 @@
}
/** @hide */
+ public int performHandwritingSelectGesture(@NonNull SelectGesture gesture) {
+ int[] range = getRangeForRect(gesture.getSelectionArea(), gesture.getGranularity());
+ if (range == null) {
+ return handleGestureFailure(gesture);
+ }
+ Selection.setSelection(getEditableText(), range[0], range[1]);
+ mEditor.startSelectionActionModeAsync(/* adjustSelection= */ false);
+ return 0;
+ }
+
+ /** @hide */
+ public int performHandwritingDeleteGesture(@NonNull DeleteGesture gesture) {
+ int[] range = getRangeForRect(gesture.getDeletionArea(), gesture.getGranularity());
+ if (range == null) {
+ return handleGestureFailure(gesture);
+ }
+ getEditableText().delete(range[0], range[1]);
+ Selection.setSelection(getEditableText(), range[0]);
+ // TODO: Delete extra spaces.
+ return 0;
+ }
+
+ /** @hide */
+ public int performHandwritingInsertGesture(@NonNull InsertGesture gesture) {
+ PointF point = gesture.getInsertionPoint();
+ // The coordinates provided are screen coordinates - transform to content coordinates.
+ int[] screenToViewport = getLocationOnScreen();
+ point.offset(
+ -(screenToViewport[0] + viewportToContentHorizontalOffset()),
+ -(screenToViewport[1] + viewportToContentVerticalOffset()));
+
+ int line = mLayout.getLineForVertical((int) point.y);
+ if (point.y < mLayout.getLineTop(line)
+ || point.y > mLayout.getLineBottomWithoutSpacing(line)) {
+ return handleGestureFailure(gesture);
+ }
+ if (point.x < mLayout.getLineLeft(line) || point.x > mLayout.getLineRight(line)) {
+ return handleGestureFailure(gesture);
+ }
+ int offset = mLayout.getOffsetForHorizontal(line, point.x);
+ String textToInsert = gesture.getTextToInsert();
+ getEditableText().insert(offset, textToInsert);
+ Selection.setSelection(getEditableText(), offset + textToInsert.length());
+ // TODO: Insert extra spaces if necessary.
+ return 0;
+ }
+
+ private int handleGestureFailure(HandwritingGesture gesture) {
+ if (!TextUtils.isEmpty(gesture.getFallbackText())) {
+ getEditableText()
+ .replace(getSelectionStart(), getSelectionEnd(), gesture.getFallbackText());
+ }
+ return 0;
+ }
+
+ @Nullable
+ private int[] getRangeForRect(@NonNull RectF area, int granularity) {
+ // The coordinates provided are screen coordinates - transform to content coordinates.
+ int[] screenToViewport = getLocationOnScreen();
+ area = new RectF(area);
+ area.offset(
+ -(screenToViewport[0] + viewportToContentHorizontalOffset()),
+ -(screenToViewport[1] + viewportToContentVerticalOffset()));
+
+ SegmentIterator segmentIterator;
+ if (granularity == HandwritingGesture.GRANULARITY_WORD) {
+ WordIterator wordIterator = getWordIterator();
+ wordIterator.setCharSequence(mText, 0, mText.length());
+ segmentIterator = new WordSegmentIterator(mText, wordIterator);
+ } else {
+ segmentIterator = new GraphemeClusterSegmentIterator(mText, mTextPaint);
+ }
+
+ return mLayout.getRangeForRect(area, segmentIterator);
+ }
+
+ /** @hide */
@VisibleForTesting
@UnsupportedAppUsage
public void nullLayouts() {
@@ -11480,6 +11570,7 @@
MotionEvent.actionToString(event.getActionMasked()),
event.getX(), event.getY());
}
+ mLastInputSource = event.getSource();
final int action = event.getActionMasked();
if (mEditor != null) {
if (!isFromPrimePointer(event, false)) {
@@ -11569,6 +11660,17 @@
}
/**
+ * Returns true when need to show UIs, e.g. floating toolbar, etc, for finger based interaction.
+ *
+ * @return true if UIs need to show for finger interaciton. false if UIs are not necessary.
+ * @hide
+ */
+ public final boolean showUIForTouchScreen() {
+ return (mLastInputSource & InputDevice.SOURCE_TOUCHSCREEN)
+ == InputDevice.SOURCE_TOUCHSCREEN;
+ }
+
+ /**
* The fill dialog UI is a more conspicuous and efficient interface than dropdown UI.
* If autofill suggestions are available when the user clicks on a field that supports filling
* the dialog UI, Autofill will pop up a fill dialog. The dialog will take up a larger area
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index be7501f..9211926 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -22,6 +22,9 @@
import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END;
import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.text.Editable;
import android.text.Selection;
@@ -31,12 +34,19 @@
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.DumpableInputConnection;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.SelectGesture;
import android.widget.TextView;
+import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
+
/**
* Base class for an editable InputConnection instance. This is created by {@link TextView} or
* {@link android.widget.EditText}.
@@ -257,6 +267,25 @@
}
@Override
+ public void performHandwritingGesture(
+ @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
+ @Nullable IntConsumer consumer) {
+ int result;
+ if (gesture instanceof SelectGesture) {
+ result = mTextView.performHandwritingSelectGesture((SelectGesture) gesture);
+ } else if (gesture instanceof DeleteGesture) {
+ result = mTextView.performHandwritingDeleteGesture((DeleteGesture) gesture);
+ } else if (gesture instanceof InsertGesture) {
+ result = mTextView.performHandwritingInsertGesture((InsertGesture) gesture);
+ } else {
+ result = 0;
+ }
+ if (executor != null && consumer != null) {
+ executor.execute(() -> consumer.accept(result));
+ }
+ }
+
+ @Override
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
CharSequence editableText = mTextView.getText();
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 33cb56f..b5fc05b 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -227,6 +227,8 @@
return "HIDE_DOCKED_STACK_ATTACHED";
case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
return "HIDE_RECENTS_ANIMATION";
+ case SoftInputShowHideReason.HIDE_BUBBLES:
+ return "HIDE_BUBBLES";
case SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR:
return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR";
case SoftInputShowHideReason.HIDE_REMOVE_CLIENT:
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index b11ea29..9ef7ce38 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -19,6 +19,8 @@
import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.ImageDecoder;
@@ -109,13 +111,13 @@
}
break;
case Icon.TYPE_RESOURCE:
- if (!(TextUtils.isEmpty(icon.getResPackage())
- || context.getPackageName().equals(icon.getResPackage()))) {
- // We can't properly resolve icons from other packages here, so fall back.
+ Resources res = resolveResourcesForIcon(context, icon);
+ if (res == null) {
+ // We couldn't resolve resources properly, fall back to icon loading.
return icon.loadDrawable(context);
}
- Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight);
+ Drawable result = resolveImage(res, icon.getResId(), maxWidth, maxHeight);
if (result != null) {
return tintDrawable(icon, result);
}
@@ -159,6 +161,13 @@
}
@Nullable
+ private static Drawable resolveImage(Resources res, @DrawableRes int resId, int maxWidth,
+ int maxHeight) {
+ final ImageDecoder.Source source = ImageDecoder.createSource(res, resId);
+ return resolveImage(source, maxWidth, maxHeight);
+ }
+
+ @Nullable
private static Drawable resolveBitmapImage(Icon icon, Context context, int maxWidth,
int maxHeight) {
@@ -259,4 +268,52 @@
}
return icon.getUri();
}
+
+ /**
+ * Resolves the correct resources package for a given Icon - it may come from another
+ * package.
+ *
+ * @see Icon#loadDrawableInner(Context)
+ * @hide
+ *
+ * @return resources instance if the operation succeeded, null otherwise
+ */
+ @Nullable
+ @VisibleForTesting
+ public static Resources resolveResourcesForIcon(Context context, Icon icon) {
+ if (icon.getType() != Icon.TYPE_RESOURCE) {
+ return null;
+ }
+
+ // Icons cache resolved resources, use cache if available.
+ Resources res = icon.getResources();
+ if (res != null) {
+ return res;
+ }
+
+ String resPackage = icon.getResPackage();
+ // No package means we try to use current context.
+ if (TextUtils.isEmpty(resPackage) || context.getPackageName().equals(resPackage)) {
+ return context.getResources();
+ }
+
+ if ("android".equals(resPackage)) {
+ return Resources.getSystem();
+ }
+
+ final PackageManager pm = context.getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(resPackage,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.GET_SHARED_LIBRARY_FILES);
+ if (ai != null) {
+ return pm.getResourcesForApplication(ai);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, String.format("Unable to resolve package %s for icon %s", resPackage, icon));
+ return null;
+ }
+
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 9f3f335..03e7fd1 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -26,7 +26,6 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.storage.StorageManager;
import android.text.TextUtils;
import com.android.internal.util.ArrayUtils;
@@ -347,7 +346,7 @@
@Override
public int hashCode() {
// Effective Java — Always override hashCode when you override equals
- return (17 + mType) * 31 + mCredential.hashCode();
+ return Objects.hash(mType, Arrays.hashCode(mCredential));
}
@Override
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 7f50204..14699e7 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -95,3 +95,7 @@
# Battery
per-file com_android_internal_os_Kernel* = file:/BATTERY_STATS_OWNERS
per-file com_android_internal_os_*MultiStateCounter* = file:/BATTERY_STATS_OWNERS
+
+# PM
+per-file com_android_internal_content_* = file:/PACKAGE_MANAGER_OWNERS
+
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index be82879..acf4da6 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -17,28 +17,25 @@
#define LOG_TAG "NativeLibraryHelper"
//#define LOG_NDEBUG 0
-#include "core_jni_helpers.h"
-
-#include <nativehelper/ScopedUtfChars.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
-#include <utils/Log.h>
-#include <utils/Vector.h>
-
-#include <zlib.h>
-
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <nativehelper/ScopedUtfChars.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include <zlib.h>
#include <memory>
+#include "core_jni_helpers.h"
+
#define APK_LIB "lib/"
#define APK_LIB_LEN (sizeof(APK_LIB) - 1)
@@ -156,7 +153,7 @@
size_t* total = (size_t*) arg;
uint32_t uncompLen;
- if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, NULL, NULL)) {
+ if (!zipFile->getEntryInfo(zipEntry, nullptr, &uncompLen, nullptr, nullptr, nullptr, nullptr)) {
return INSTALL_FAILED_INVALID_APK;
}
@@ -186,7 +183,7 @@
uint16_t method;
off64_t offset;
- if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) {
+ if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc)) {
ALOGE("Couldn't read zip entry info\n");
return INSTALL_FAILED_INVALID_APK;
}
@@ -307,24 +304,24 @@
class NativeLibrariesIterator {
private:
NativeLibrariesIterator(ZipFileRO* zipFile, bool debuggable, void* cookie)
- : mZipFile(zipFile), mDebuggable(debuggable), mCookie(cookie), mLastSlash(NULL) {
+ : mZipFile(zipFile), mDebuggable(debuggable), mCookie(cookie), mLastSlash(nullptr) {
fileName[0] = '\0';
}
public:
static NativeLibrariesIterator* create(ZipFileRO* zipFile, bool debuggable) {
- void* cookie = NULL;
+ void* cookie = nullptr;
// Do not specify a suffix to find both .so files and gdbserver.
- if (!zipFile->startIteration(&cookie, APK_LIB, NULL /* suffix */)) {
- return NULL;
+ if (!zipFile->startIteration(&cookie, APK_LIB, nullptr /* suffix */)) {
+ return nullptr;
}
return new NativeLibrariesIterator(zipFile, debuggable, cookie);
}
ZipEntryRO next() {
- ZipEntryRO next = NULL;
- while ((next = mZipFile->nextEntry(mCookie)) != NULL) {
+ ZipEntryRO next = nullptr;
+ while ((next = mZipFile->nextEntry(mCookie)) != nullptr) {
// Make sure this entry has a filename.
if (mZipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
continue;
@@ -338,7 +335,7 @@
}
const char* lastSlash = strrchr(fileName, '/');
- ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName);
+ ALOG_ASSERT(lastSlash != nullptr, "last slash was null somehow for %s\n", fileName);
// Skip directories.
if (*(lastSlash + 1) == 0) {
@@ -389,24 +386,23 @@
iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi,
jboolean debuggable, iterFunc callFunc, void* callArg) {
ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
- if (zipFile == NULL) {
+ if (zipFile == nullptr) {
return INSTALL_FAILED_INVALID_APK;
}
std::unique_ptr<NativeLibrariesIterator> it(
NativeLibrariesIterator::create(zipFile, debuggable));
- if (it.get() == NULL) {
+ if (it.get() == nullptr) {
return INSTALL_FAILED_INVALID_APK;
}
const ScopedUtfChars cpuAbi(env, javaCpuAbi);
- if (cpuAbi.c_str() == NULL) {
- // This would've thrown, so this return code isn't observable by
- // Java.
+ if (cpuAbi.c_str() == nullptr) {
+ // This would've thrown, so this return code isn't observable by Java.
return INSTALL_FAILED_INVALID_APK;
}
- ZipEntryRO entry = NULL;
- while ((entry = it->next()) != NULL) {
+ ZipEntryRO entry = nullptr;
+ while ((entry = it->next()) != nullptr) {
const char* fileName = it->currentEntry();
const char* lastSlash = it->lastSlash();
@@ -427,31 +423,30 @@
return INSTALL_SUCCEEDED;
}
-
-static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray,
- jboolean debuggable) {
- const int numAbis = env->GetArrayLength(supportedAbisArray);
- Vector<ScopedUtfChars*> supportedAbis;
-
- for (int i = 0; i < numAbis; ++i) {
- supportedAbis.add(new ScopedUtfChars(env,
- (jstring) env->GetObjectArrayElement(supportedAbisArray, i)));
- }
-
+static int findSupportedAbi(JNIEnv* env, jlong apkHandle, jobjectArray supportedAbisArray,
+ jboolean debuggable) {
ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
- if (zipFile == NULL) {
+ if (zipFile == nullptr) {
return INSTALL_FAILED_INVALID_APK;
}
std::unique_ptr<NativeLibrariesIterator> it(
NativeLibrariesIterator::create(zipFile, debuggable));
- if (it.get() == NULL) {
+ if (it.get() == nullptr) {
return INSTALL_FAILED_INVALID_APK;
}
- ZipEntryRO entry = NULL;
+ const int numAbis = env->GetArrayLength(supportedAbisArray);
+
+ std::vector<ScopedUtfChars> supportedAbis;
+ supportedAbis.reserve(numAbis);
+ for (int i = 0; i < numAbis; ++i) {
+ supportedAbis.emplace_back(env, (jstring)env->GetObjectArrayElement(supportedAbisArray, i));
+ }
+
+ ZipEntryRO entry = nullptr;
int status = NO_NATIVE_LIBRARIES;
- while ((entry = it->next()) != NULL) {
+ while ((entry = it->next()) != nullptr) {
// We're currently in the lib/ directory of the APK, so it does have some native
// code. We should return INSTALL_FAILED_NO_MATCHING_ABIS if none of the
// libraries match.
@@ -466,8 +461,8 @@
const char* abiOffset = fileName + APK_LIB_LEN;
const size_t abiSize = lastSlash - abiOffset;
for (int i = 0; i < numAbis; i++) {
- const ScopedUtfChars* abi = supportedAbis[i];
- if (abi->size() == abiSize && !strncmp(abiOffset, abi->c_str(), abiSize)) {
+ const ScopedUtfChars& abi = supportedAbis[i];
+ if (abi.size() == abiSize && !strncmp(abiOffset, abi.c_str(), abiSize)) {
// The entry that comes in first (i.e. with a lower index) has the higher priority.
if (((i < status) && (status >= 0)) || (status < 0) ) {
status = i;
@@ -476,10 +471,6 @@
}
}
- for (int i = 0; i < numAbis; ++i) {
- delete supportedAbis[i];
- }
-
return status;
}
@@ -521,19 +512,19 @@
com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *env, jclass clazz,
jlong apkHandle) {
ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
- void* cookie = NULL;
- if (!zipFile->startIteration(&cookie, NULL /* prefix */, RS_BITCODE_SUFFIX)) {
+ void* cookie = nullptr;
+ if (!zipFile->startIteration(&cookie, nullptr /* prefix */, RS_BITCODE_SUFFIX)) {
return APK_SCAN_ERROR;
}
char fileName[PATH_MAX];
- ZipEntryRO next = NULL;
- while ((next = zipFile->nextEntry(cookie)) != NULL) {
+ ZipEntryRO next = nullptr;
+ while ((next = zipFile->nextEntry(cookie)) != nullptr) {
if (zipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
continue;
}
const char* lastSlash = strrchr(fileName, '/');
- const char* baseName = (lastSlash == NULL) ? fileName : fileName + 1;
+ const char* baseName = (lastSlash == nullptr) ? fileName : fileName + 1;
if (isFilenameSafe(baseName)) {
zipFile->endIteration(cookie);
return BITCODE_PRESENT;
diff --git a/core/proto/android/app/notificationmanager.proto b/core/proto/android/app/notificationmanager.proto
index b88315e..3342c79 100644
--- a/core/proto/android/app/notificationmanager.proto
+++ b/core/proto/android/app/notificationmanager.proto
@@ -45,6 +45,8 @@
MEDIA = 7;
// System (catch-all for non-never suppressible sounds) are prioritized.
SYSTEM = 8;
+ // Priority conversations are prioritized
+ CONVERSATIONS = 9;
}
repeated Category priority_categories = 1;
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 93ce783..7e17840 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -130,6 +130,10 @@
// Allow overlay to add resource
"--auto-add-overlay",
+
+ // Framework resources benefit tremendously from enabling sparse encoding, saving tens
+ // of MBs in size and RAM use.
+ "--enable-sparse-encoding",
],
resource_zips: [
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6a52ec9..e7cae76 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1565,7 +1565,8 @@
<bool name="config_enableIdleScreenBrightnessMode">false</bool>
<!-- Array of desired screen brightness in nits corresponding to the lux values
- in the config_autoBrightnessLevels array. The display brightness is defined as the measured
+ in the config_autoBrightnessLevels array. As with config_screenBrightnessMinimumNits and
+ config_screenBrightnessMaximumNits, the display brightness is defined as the measured
brightness of an all-white image.
If this is defined then:
@@ -1586,7 +1587,7 @@
<array name="config_autoBrightnessDisplayValuesNitsIdle">
</array>
- <!-- Array of output values for button backlight corresponding to the lux values
+ <!-- Array of output values for button backlight corresponding to the luX values
in the config_autoBrightnessLevels array. This array should have size one greater
than the size of the config_autoBrightnessLevels array.
The brightness values must be between 0 and 255 and be non-decreasing.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e5b1cf9..8488b68 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1733,7 +1733,7 @@
<!-- Generic error message shown when the fingerprint operation fails because too many attempts have been made. -->
<string name="fingerprint_error_lockout">Too many attempts. Try again later.</string>
<!-- Generic error message shown when the fingerprint operation fails because strong authentication is required -->
- <string name="fingerprint_error_lockout_permanent">Too many attempts. Fingerprint sensor disabled.</string>
+ <string name="fingerprint_error_lockout_permanent">Too many attempts. Use screen lock instead.</string>
<!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint -->
<string name="fingerprint_error_unable_to_process">Try again.</string>
<!-- Generic error message shown when the user has no enrolled fingerprints -->
diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java
index c5d6f7f..21a2205 100644
--- a/core/tests/coretests/src/android/provider/FontsContractTest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractTest.java
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
/**
@@ -282,9 +283,10 @@
setupPackageManager();
byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
- List<byte[]> certList = Arrays.asList(wrongCert);
+ List<byte[]> certList = Collections.singletonList(wrongCert);
FontRequest requestWrongCerts = new FontRequest(
- TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query",
+ Collections.singletonList(certList));
assertNull(FontsContract.getProvider(mPackageManager, requestWrongCerts));
}
@@ -293,9 +295,10 @@
throws PackageManager.NameNotFoundException {
ProviderInfo info = setupPackageManager();
- List<byte[]> certList = Arrays.asList(BYTE_ARRAY);
+ List<byte[]> certList = Collections.singletonList(BYTE_ARRAY);
FontRequest requestRightCerts = new FontRequest(
- TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query",
+ Collections.singletonList(certList));
ProviderInfo result = FontsContract.getProvider(
mPackageManager, requestRightCerts);
@@ -309,7 +312,8 @@
byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
List<byte[]> certList = Arrays.asList(wrongCert, BYTE_ARRAY);
FontRequest requestRightCerts = new FontRequest(
- TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query",
+ Collections.singletonList(certList));
assertNull(FontsContract.getProvider(mPackageManager, requestRightCerts));
}
@@ -332,7 +336,8 @@
// {BYTE_ARRAY_2, BYTE_ARRAY_COPY}.
List<byte[]> certList = Arrays.asList(BYTE_ARRAY_2, BYTE_ARRAY_COPY);
FontRequest requestRightCerts = new FontRequest(
- TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query",
+ Collections.singletonList(certList));
assertNull(FontsContract.getProvider(mPackageManager, requestRightCerts));
}
@@ -341,9 +346,9 @@
ProviderInfo info = setupPackageManager();
List<List<byte[]>> certList = new ArrayList<>();
- byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
- certList.add(Arrays.asList(wrongCert));
- certList.add(Arrays.asList(BYTE_ARRAY));
+ certList.add(Collections.singletonList(
+ Base64.decode("this is a wrong cert", Base64.DEFAULT)));
+ certList.add(Collections.singletonList(BYTE_ARRAY));
FontRequest requestRightCerts = new FontRequest(
TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", certList);
ProviderInfo result = FontsContract.getProvider(mPackageManager, requestRightCerts);
@@ -356,7 +361,7 @@
setupPackageManager();
List<List<byte[]>> certList = new ArrayList<>();
- certList.add(Arrays.asList(BYTE_ARRAY));
+ certList.add(Collections.singletonList(BYTE_ARRAY));
FontRequest requestRightCerts = new FontRequest(
TestFontsProvider.AUTHORITY, "com.wrong.package.name", "query", certList);
try {
diff --git a/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java b/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java
new file mode 100644
index 0000000..32fdb5e
--- /dev/null
+++ b/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java
@@ -0,0 +1,407 @@
+/*
+ * 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.text;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.text.method.WordIterator;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class LayoutGetRangeForRectTest {
+
+ private static final int WIDTH = 200;
+ private static final int HEIGHT = 1000;
+ private static final String DEFAULT_TEXT = ""
+ // Line 0 (offset 0 to 18)
+ // - Word 0 (offset 0 to 4) has bounds [0, 40], center 20
+ // - Word 1 (offset 5 to 11) has bounds [50, 110], center 80
+ // - Word 2 (offset 12 to 17) has bounds [120, 170], center 145
+ + "XXXX XXXXXX XXXXX "
+ // Line 1 (offset 18 to 36)
+ // - Word 3 (offset 18 to 23) has bounds [0, 50], center 25
+ // - Word 4 (offset 24 to 26, RTL) has bounds [100, 110], center 105
+ // - Word 5 (offset 27 to 29, RTL) has bounds [80, 90], center 85
+ // - Word 6 start part (offset 30 to 32, RTL) has bounds [60, 70], center 65
+ // - Word 6 end part (offset 32 to 35) has bounds [110, 140], center 125
+ + "XXXXX \u05D1\u05D1 \u05D1\u05D1 \u05D1\u05D1XXX\n"
+ // Line 2 (offset 36 to 38)
+ // - Word 7 start part (offset 36 to 38) has bounds [0, 150], center 75
+ // Line 3 (offset 38 to 40)
+ // - Word 7 middle part (offset 38 to 40) has bounds [0, 150], center 75
+ // Line 4 (offset 40 to 46)
+ // - Word 7 end part (offset 40 to 41) has bounds [0, 100], center 50
+ // - Word 8 (offset 42 to 44) has bounds [110, 130], center 120
+ + "CLCLC XX \n";
+
+ private Layout mLayout;
+ private float[] mLineCenters;
+ private GraphemeClusterSegmentIterator mGraphemeClusterSegmentIterator;
+ private WordSegmentIterator mWordSegmentIterator;
+
+ @Before
+ public void setup() {
+ // The test font includes the following characters:
+ // U+0020 ( ): 10em
+ // U+002E (.): 10em
+ // U+0049 (I): 1em
+ // U+0056 (V): 5em
+ // U+0058 (X): 10em
+ // U+004C (L): 50em
+ // U+0043 (C): 100em
+ // U+005F (_): 0em
+ // U+05D0 : 1em // HEBREW LETTER ALEF
+ // U+05D1 : 5em // HEBREW LETTER BET
+ // U+FFFD (invalid surrogate will be replaced to this): 7em
+ // U+10331 (\uD800\uDF31): 10em
+ // Undefined : 0.5em
+ TextPaint textPaint = new TextPaint();
+ textPaint.setTypeface(
+ Typeface.createFromAsset(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets(),
+ "fonts/StaticLayoutLineBreakingTestFont.ttf"));
+ // Make 1 em equal to 1 pixel.
+ textPaint.setTextSize(1.0f);
+
+ mLayout = StaticLayout.Builder.obtain(
+ DEFAULT_TEXT, 0, DEFAULT_TEXT.length(), textPaint, WIDTH).build();
+
+ mLineCenters = new float[mLayout.getLineCount()];
+ for (int i = 0; i < mLayout.getLineCount(); ++i) {
+ mLineCenters[i] = (mLayout.getLineTop(i) + mLayout.getLineBottomWithoutSpacing(i)) / 2f;
+ }
+
+ mGraphemeClusterSegmentIterator =
+ new GraphemeClusterSegmentIterator(DEFAULT_TEXT, textPaint);
+ WordIterator wordIterator = new WordIterator();
+ wordIterator.setCharSequence(DEFAULT_TEXT, 0, DEFAULT_TEXT.length());
+ mWordSegmentIterator = new WordSegmentIterator(DEFAULT_TEXT, wordIterator);
+ }
+
+ @Test
+ public void getRangeForRect_character() {
+ // Character 1 on line 0 has center 15.
+ // Character 2 on line 0 has center 25.
+ RectF area = new RectF(14f, mLineCenters[0] - 1f, 26f, mLineCenters[0] + 1f);
+
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ assertThat(range).asList().containsExactly(1, 3).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_character_partialCharacterButNotCenter() {
+ // Character 0 on line 0 has center 5.
+ // Character 1 on line 0 has center 15.
+ // Character 2 on line 0 has center 25.
+ // Character 3 on line 0 has center 35.
+ RectF area = new RectF(6f, mLineCenters[0] - 1f, 34f, mLineCenters[0] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ // Area partially overlaps characters 0 and 3 but does not contain their centers.
+ assertThat(range).asList().containsExactly(1, 3).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_character_rtl() {
+ // Character 25 on line 1 has center 102.5.
+ // Character 26 on line 1 has center 95.
+ RectF area = new RectF(94f, mLineCenters[1] - 1f, 103f, mLineCenters[1] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ assertThat(range).asList().containsExactly(25, 27).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_character_ltrAndRtl() {
+ // Character 22 on line 1 has center 45.
+ // The end of the RTL run (offset 24 to 32) on line 1 is at 60.
+ RectF area = new RectF(44f, mLineCenters[1] - 1f, 93f, mLineCenters[1] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ assertThat(range).asList().containsExactly(22, 32).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_character_rtlAndLtr() {
+ // The start of the RTL run (offset 24 to 32) on line 1 is at 110.
+ // Character 33 on line 1 has center 125.
+ RectF area = new RectF(93f, mLineCenters[1] - 1f, 131f, mLineCenters[1] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ assertThat(range).asList().containsExactly(24, 34).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_character_betweenCharacters_shouldFallback() {
+ // Character 1 on line 0 has center 15.
+ // Character 2 on line 0 has center 25.
+ RectF area = new RectF(16f, mLineCenters[0] - 1f, 24f, mLineCenters[0] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ assertThat(range).isNull();
+ }
+
+ @Test
+ public void getRangeForRect_character_betweenLines_shouldFallback() {
+ // Area top is below the center of line 0.
+ // Area bottom is above the center of line 1.
+ RectF area = new RectF(0f, mLineCenters[0] + 1f, WIDTH, mLineCenters[1] - 1f);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ // Area partially covers two lines but does not contain the center of any characters.
+ assertThat(range).isNull();
+ }
+
+ @Test
+ public void getRangeForRect_character_multiLine() {
+ // Character 9 on line 0 has center 95.
+ // Character 42 on line 4 has center 115.
+ RectF area = new RectF(93f, 0, 118f, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ assertThat(range).asList().containsExactly(9, 43).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_character_multiLine_betweenCharactersOnSomeLines() {
+ // Character 6 on line 0 has center 65.
+ // Character 7 on line 0 has center 75.
+ // Character 30 on line 1 has center 67.5.
+ // Character 36 on line 2 has center 50.
+ // Character 37 on line 2 has center 125.
+ // Character 38 on line 3 has center 50.
+ // Character 39 on line 3 has center 125.
+ // Character 40 on line 4 has center 50.
+ // Character 41 on line 4 has center 105.
+ RectF area = new RectF(66f, 0, 69f, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ // Area crosses all lines but does not contain the center of any characters on lines 0, 2,
+ // 3, or 4. So the only included character is character 30 on line 1.
+ assertThat(range).asList().containsExactly(30, 31).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_character_multiLine_betweenCharactersOnAllLines_shouldFallback() {
+ // Character 6 on line 0 has center 65.
+ // Character 7 on line 0 has center 75.
+ // Character 30 on line 1 has center 67.5.
+ // Character 31 on line 1 has center 62.5.
+ // Character 36 on line 2 has center 50.
+ // Character 37 on line 2 has center 125.
+ // Character 38 on line 3 has center 50.
+ // Character 39 on line 3 has center 125.
+ // Character 40 on line 4 has center 50.
+ // Character 41 on line 4 has center 105.
+ RectF area = new RectF(66f, 0, 67f, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ // Area crosses all lines but does not contain the center of any characters.
+ assertThat(range).isNull();
+ }
+
+ @Test
+ public void getRangeForRect_character_all() {
+ // Entire area, should include all text.
+ RectF area = new RectF(0f, 0f, WIDTH, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator);
+
+ assertThat(range).asList().containsExactly(0, DEFAULT_TEXT.length()).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word() {
+ // Word 1 (offset 5 to 11) on line 0 has center 80.
+ RectF area = new RectF(79f, mLineCenters[0] - 1f, 81f, mLineCenters[0] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ assertThat(range).asList().containsExactly(5, 11).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_partialWordButNotCenter() {
+ // Word 0 (offset 0 to 4) on line 0 has center 20.
+ // Word 1 (offset 5 to 11) on line 0 has center 80.
+ // Word 2 (offset 12 to 17) on line 0 center 145
+ RectF area = new RectF(21f, mLineCenters[0] - 1f, 144f, mLineCenters[0] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Area partially overlaps words 0 and 2 but does not contain their centers, so only word 1
+ // is included. Whitespace between words is not included.
+ assertThat(range).asList().containsExactly(5, 11).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_rtl() {
+ // Word 4 (offset 24 to 26, RTL) on line 1 center 105
+ RectF area = new RectF(88f, mLineCenters[1] - 1f, 119f, mLineCenters[1] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ assertThat(range).asList().containsExactly(24, 26).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_ltrAndRtl() {
+ // Word 3 (offset 18 to 23) on line 1 has center 25
+ // The end of the RTL run (offset 24 to 32) on line 1 is at 60.
+ RectF area = new RectF(24f, mLineCenters[1] - 1f, 93f, mLineCenters[1] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Selects all of word 6, not just the first RTL part.
+ assertThat(range).asList().containsExactly(18, 35).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_rtlAndLtr() {
+ // The start of the RTL run (offset 24 to 32) on line 1 is at 110.
+ // End part of word 6 (offset 32 to 35) on line 1 has center 125.
+ RectF area = new RectF(93f, mLineCenters[1] - 1f, 174f, mLineCenters[1] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ assertThat(range).asList().containsExactly(24, 35).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_betweenWords_shouldFallback() {
+ // Word 1 on line 0 has center 80.
+ // Word 2 on line 0 has center 145.
+ RectF area = new RectF(81f, mLineCenters[0] - 1f, 144f, mLineCenters[0] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ assertThat(range).isNull();
+ }
+
+ @Test
+ public void getRangeForRect_word_betweenLines_shouldFallback() {
+ // Area top is below the center of line 0.
+ // Area bottom is above the center of line 1.
+ RectF area = new RectF(0f, mLineCenters[0] + 1f, WIDTH, mLineCenters[1] - 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Area partially covers two lines but does not contain the center of any words.
+ assertThat(range).isNull();
+ }
+
+ @Test
+ public void getRangeForRect_word_multiLine() {
+ // Word 1 (offset 5 to 11) on line 0 has center 80.
+ // End part of word 7 (offset 40 to 41) on line 4 has center 50.
+ RectF area = new RectF(42f, 0, 91f, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ assertThat(range).asList().containsExactly(5, 41).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_multiLine_betweenWordsOnSomeLines() {
+ // Word 1 on line 0 has center 80.
+ // Word 2 on line 0 has center 145.
+ // Word 5 (offset 27 to 29) on line 1 has center 85.
+ // Word 7 on line 2 has center 50.
+ // Word 37 on line 2 has center 125.
+ // Word 38 on line 3 has center 50.
+ // Word 39 on line 3 has center 125.
+ // Word 40 on line 4 has center 50.
+ // Word 41 on line 4 has center 105.
+ RectF area = new RectF(84f, 0, 86f, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Area crosses all lines but does not contain the center of any words on lines 0, 2, 3, or
+ // 4. So the only included word is word 5 on line 1.
+ assertThat(range).asList().containsExactly(27, 29).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_multiLine_betweenCharactersOnAllLines_shouldFallback() {
+ // Word 1 on line 0 has center 80.
+ // Word 2 on line 0 has center 145.
+ // Word 4 on line 1 has center 105.
+ // Word 5 on line 1 has center 85.
+ // Word 7 on line 2 has center 50.
+ // Word 37 on line 2 has center 125.
+ // Word 38 on line 3 has center 50.
+ // Word 39 on line 3 has center 125.
+ // Word 40 on line 4 has center 50.
+ // Word 41 on line 4 has center 105.
+ RectF area = new RectF(86f, 0, 89f, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Area crosses all lines but does not contain the center of any words.
+ assertThat(range).isNull();
+ }
+
+ @Test
+ public void getRangeForRect_word_wordSpansMultipleLines_firstPartInsideArea() {
+ // Word 5 (offset 27 to 29) on line 1 has center 85.
+ // First part of word 7 (offset 36 to 38) on line 2 has center 75.
+ RectF area = new RectF(74f, mLineCenters[1] - 1f, 86f, mLineCenters[2] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Selects all of word 7, not just the first part on line 2.
+ assertThat(range).asList().containsExactly(27, 41).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_wordSpansMultipleLines_middlePartInsideArea() {
+ // Middle part of word 7 (offset 38 to 40) on line 2 has center 75.
+ RectF area = new RectF(74f, mLineCenters[3] - 1f, 75f, mLineCenters[3] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Selects all of word 7, not just the middle part on line 3.
+ assertThat(range).asList().containsExactly(36, 41).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_wordSpansMultipleLines_endPartInsideArea() {
+ // End part of word 7 (offset 40 to 41) on line 4 has center 50.
+ // Word 8 (offset 42 to 44) on line 4 has center 120
+ RectF area = new RectF(49f, mLineCenters[4] - 1f, 121f, mLineCenters[4] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Selects all of word 7, not just the middle part on line 3.
+ assertThat(range).asList().containsExactly(36, 44).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_wordSpansMultipleRuns_firstPartInsideArea() {
+ // Word 5 (offset 27 to 29) on line 1 has center 85.
+ // First part of word 6 (offset 30 to 32) on line 1 has center 65.
+ RectF area = new RectF(64f, mLineCenters[1] - 1f, 86f, mLineCenters[1] + 1f);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ // Selects all of word 6, not just the first RTL part.
+ assertThat(range).asList().containsExactly(27, 35).inOrder();
+ }
+
+ @Test
+ public void getRangeForRect_word_all() {
+ // Entire area, should include all text except the last two whitespace characters.
+ RectF area = new RectF(0f, 0f, WIDTH, HEIGHT);
+ int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator);
+
+ assertThat(range).asList().containsExactly(0, DEFAULT_TEXT.length() - 2).inOrder();
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
index 0cee526..271a20b 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
@@ -17,6 +17,8 @@
package com.android.internal.widget;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
@@ -279,4 +281,49 @@
// This drawable must not be loaded - if it was, the code ignored the package specification.
assertThat(d).isNull();
}
+
+ @Test
+ public void resolveResourcesForIcon_notAResourceIcon_returnsNull() {
+ Icon icon = Icon.createWithContentUri(Uri.parse("some_uri"));
+ assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)).isNull();
+ }
+
+ @Test
+ public void resolveResourcesForIcon_localPackageIcon_returnsPackageResources() {
+ Icon icon = Icon.createWithResource(mContext, R.drawable.test32x24);
+ assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon))
+ .isSameInstanceAs(mContext.getResources());
+ }
+
+ @Test
+ public void resolveResourcesForIcon_iconWithoutPackageSpecificed_returnsPackageResources() {
+ Icon icon = Icon.createWithResource("", R.drawable.test32x24);
+ assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon))
+ .isSameInstanceAs(mContext.getResources());
+ }
+
+ @Test
+ public void resolveResourcesForIcon_systemPackageSpecified_returnsSystemPackage() {
+ Icon icon = Icon.createWithResource("android", R.drawable.test32x24);
+ assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)).isSameInstanceAs(
+ Resources.getSystem());
+ }
+
+ @Test
+ public void resolveResourcesForIcon_differentPackageSpecified_returnsPackageResources() throws
+ PackageManager.NameNotFoundException {
+ String pkg = "com.android.settings";
+ Resources res = mContext.getPackageManager().getResourcesForApplication(pkg);
+ int resId = res.getIdentifier("ic_android", "drawable", pkg);
+ Icon icon = Icon.createWithResource(pkg, resId);
+
+ assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon).getDrawable(resId,
+ mContext.getTheme())).isNotNull();
+ }
+
+ @Test
+ public void resolveResourcesForIcon_invalidPackageSpecified_returnsNull() {
+ Icon icon = Icon.createWithResource("invalid.package", R.drawable.test32x24);
+ assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)).isNull();
+ }
}
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index f030d80..e0e13f5 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -81,5 +81,6 @@
<permission name="android.permission.READ_DEVICE_CONFIG" />
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
<permission name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS" />
+ <permission name="android.permission.READ_SEARCH_INDEXABLES" />
</privapp-permissions>
</permissions>
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 582488f..ca3c847 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -2796,7 +2796,8 @@
if (mWhitePoint == null || mTransform == null) {
throw new IllegalStateException(
"ColorSpace (" + this + ") cannot create native object! mWhitePoint: "
- + mWhitePoint + " mTransform: " + mTransform);
+ + Arrays.toString(mWhitePoint) + " mTransform: "
+ + Arrays.toString(mTransform));
}
// This mimics the old code that was in native.
diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt
deleted file mode 100644
index c7062e0..0000000
--- a/ktfmt_includes.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-packages/SystemUI/compose/
-packages/SystemUI/screenshot/
-packages/SystemUI/src/com/android/systemui/people/data
-packages/SystemUI/src/com/android/systemui/people/ui
-packages/SystemUI/src/com/android/systemui/keyguard/data
-packages/SystemUI/src/com/android/systemui/keyguard/dagger
-packages/SystemUI/src/com/android/systemui/keyguard/domain
-packages/SystemUI/src/com/android/systemui/keyguard/shared
-packages/SystemUI/src/com/android/systemui/keyguard/ui
-packages/SystemUI/src/com/android/systemui/qs/footer
-packages/SystemUI/src/com/android/systemui/security
-packages/SystemUI/src/com/android/systemui/common/
-packages/SystemUI/tests/utils/src/com/android/systemui/qs/
-packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
-packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeUserInfoController.kt
-packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/MockUserSwitcherControllerWrapper.kt
-packages/SystemUI/tests/src/com/android/systemui/qs/footer/
\ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 02af916..4102732 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -792,6 +792,9 @@
* Checks if there is a rule to split the two activities. If there is one, puts them into split
* and returns {@code true}. Otherwise, returns {@code false}.
*/
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
private boolean putActivitiesIntoSplitIfNecessary(@NonNull WindowContainerTransaction wct,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 2b069d7..2ef8e4c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -38,6 +38,7 @@
import android.view.WindowMetrics;
import android.window.WindowContainerTransaction;
+import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -171,6 +172,7 @@
* created and the activity will be re-parented to it.
* @param rule The split rule to be applied to the container.
*/
+ @GuardedBy("mController.mLock")
void createNewSplitContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity,
@NonNull SplitPairRule rule) {
@@ -187,8 +189,10 @@
final TaskFragmentContainer curSecondaryContainer = mController.getContainerWithActivity(
secondaryActivity);
TaskFragmentContainer containerToAvoid = primaryContainer;
- if (rule.shouldClearTop() && curSecondaryContainer != null) {
- // Do not reuse the current TaskFragment if the rule is to clear top.
+ if (curSecondaryContainer != null
+ && (rule.shouldClearTop() || primaryContainer.isAbove(curSecondaryContainer))) {
+ // Do not reuse the current TaskFragment if the rule is to clear top, or if it is below
+ // the primary TaskFragment.
containerToAvoid = curSecondaryContainer;
}
final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 77e26c0..45645b2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -162,4 +162,8 @@
}
return null;
}
+
+ int indexOf(@NonNull TaskFragmentContainer child) {
+ return mContainers.indexOf(child);
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 11c0db3..344ffc7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -501,6 +501,18 @@
return new Size(maxMinWidth, maxMinHeight);
}
+ /** Whether the current TaskFragment is above the {@code other} TaskFragment. */
+ boolean isAbove(@NonNull TaskFragmentContainer other) {
+ if (mTaskContainer != other.mTaskContainer) {
+ throw new IllegalArgumentException(
+ "Trying to compare two TaskFragments in different Task.");
+ }
+ if (this == other) {
+ throw new IllegalArgumentException("Trying to compare a TaskFragment with itself.");
+ }
+ return mTaskContainer.indexOf(this) > mTaskContainer.indexOf(other);
+ }
+
@Override
public String toString() {
return toString(true /* includeContainersToFinishOnExit */);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index da724d9..25f0e25 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -39,6 +39,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -247,6 +248,26 @@
verify(mPresenter).expandTaskFragment(mTransaction, secondaryTf.getTaskFragmentToken());
}
+ @Test
+ public void testCreateNewSplitContainer_secondaryAbovePrimary() {
+ final Activity secondaryActivity = createMockActivity();
+ final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID);
+ final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
+ final SplitPairRule rule = new SplitPairRule.Builder(pair ->
+ pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
+ metrics -> true)
+ .setShouldClearTop(false)
+ .build();
+
+ mPresenter.createNewSplitContainer(mTransaction, mActivity, secondaryActivity, rule);
+
+ assertEquals(primaryTf, mController.getContainerWithActivity(mActivity));
+ final TaskFragmentContainer secondaryTf = mController.getContainerWithActivity(
+ secondaryActivity);
+ assertNotEquals(bottomTf, secondaryTf);
+ assertTrue(secondaryTf.isAbove(primaryTf));
+ }
+
private Activity createMockActivity() {
final Activity activity = mock(Activity.class);
final Configuration activityConfig = new Configuration();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 6cbecff..1bc81ee 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -288,6 +288,18 @@
}
@Test
+ public void testIsAbove() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskFragmentContainer container0 = new TaskFragmentContainer(null /* activity */,
+ mIntent, taskContainer, mController);
+ final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
+ mIntent, taskContainer, mController);
+
+ assertTrue(container1.isAbove(container0));
+ assertFalse(container0.isAbove(container1));
+ }
+
+ @Test
public void testGetBottomMostActivity() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml
deleted file mode 100644
index 6fcd1de..0000000
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/letterbox_education_dialog_icon_size"
- android:height="@dimen/letterbox_education_dialog_icon_size"
- android:viewportWidth="48"
- android:viewportHeight="48">
- <path
- android:fillColor="@color/letterbox_education_accent_primary"
- android:fillType="evenOdd"
- android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
- <path
- android:fillColor="@color/letterbox_education_accent_primary"
- android:pathData="M 17 14 L 31 14 Q 32 14 32 15 L 32 33 Q 32 34 31 34 L 17 34 Q 16 34 16 33 L 16 15 Q 16 14 17 14 Z" />
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml
new file mode 100644
index 0000000..ddfb5c2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/letterbox_education_dialog_title_icon_width"
+ android:height="@dimen/letterbox_education_dialog_title_icon_height"
+ android:viewportWidth="45"
+ android:viewportHeight="44">
+
+ <path
+ android:fillColor="@color/letterbox_education_accent_primary"
+ android:pathData="M11 40H19C19 42.2 17.2 44 15 44C12.8 44 11 42.2 11 40ZM7 38L23 38V34L7 34L7 38ZM30 19C30 26.64 24.68 30.72 22.46 32L7.54 32C5.32 30.72 0 26.64 0 19C0 10.72 6.72 4 15 4C23.28 4 30 10.72 30 19ZM26 19C26 12.94 21.06 8 15 8C8.94 8 4 12.94 4 19C4 23.94 6.98 26.78 8.7 28L21.3 28C23.02 26.78 26 23.94 26 19ZM39.74 14.74L37 16L39.74 17.26L41 20L42.26 17.26L45 16L42.26 14.74L41 12L39.74 14.74ZM35 12L36.88 7.88L41 6L36.88 4.12L35 0L33.12 4.12L29 6L33.12 7.88L35 12Z" />
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml
index cbfcfd0..22a8f39 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml
@@ -15,18 +15,16 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/letterbox_education_dialog_icon_size"
- android:height="@dimen/letterbox_education_dialog_icon_size"
- android:viewportWidth="48"
- android:viewportHeight="48">
+ android:width="@dimen/letterbox_education_dialog_icon_width"
+ android:height="@dimen/letterbox_education_dialog_icon_height"
+ android:viewportWidth="40"
+ android:viewportHeight="32">
+
<path
android:fillColor="@color/letterbox_education_text_secondary"
android:fillType="evenOdd"
- android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
+ android:pathData="M4 0C1.79086 0 0 1.79086 0 4V28C0 30.2091 1.79086 32 4 32H36C38.2091 32 40 30.2091 40 28V4C40 1.79086 38.2091 0 36 0H4ZM36 4H4V28H36V4Z" />
<path
android:fillColor="@color/letterbox_education_text_secondary"
- android:pathData="M 14 22 H 30 V 26 H 14 V 22 Z" />
- <path
- android:fillColor="@color/letterbox_education_text_secondary"
- android:pathData="M26 16L34 24L26 32V16Z" />
+ android:pathData="M19.98 8L17.16 10.82L20.32 14L12 14V18H20.32L17.14 21.18L19.98 24L28 16.02L19.98 8Z" />
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml
deleted file mode 100644
index 469eb1e..0000000
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/letterbox_education_dialog_icon_size"
- android:height="@dimen/letterbox_education_dialog_icon_size"
- android:viewportWidth="48"
- android:viewportHeight="48">
- <path
- android:fillColor="@color/letterbox_education_text_secondary"
- android:fillType="evenOdd"
- android:pathData="M22.56 2H26C37.02 2 46 10.98 46 22H42C42 14.44 36.74 8.1 29.7 6.42L31.74 10L28.26 12L22.56 2ZM22 46H25.44L19.74 36L16.26 38L18.3 41.58C11.26 39.9 6 33.56 6 26H2C2 37.02 10.98 46 22 46ZM20.46 12L36 27.52L27.54 36L12 20.48L20.46 12ZM17.64 9.18C18.42 8.4 19.44 8 20.46 8C21.5 8 22.52 8.4 23.3 9.16L38.84 24.7C40.4 26.26 40.4 28.78 38.84 30.34L30.36 38.82C29.58 39.6 28.56 40 27.54 40C26.52 40 25.5 39.6 24.72 38.82L9.18 23.28C7.62 21.72 7.62 19.2 9.18 17.64L17.64 9.18Z" />
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml
index dcb8aed..15e65f7 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml
@@ -15,18 +15,12 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/letterbox_education_dialog_icon_size"
- android:height="@dimen/letterbox_education_dialog_icon_size"
- android:viewportWidth="48"
- android:viewportHeight="48">
+ android:width="@dimen/letterbox_education_dialog_icon_width"
+ android:height="@dimen/letterbox_education_dialog_icon_height"
+ android:viewportWidth="40"
+ android:viewportHeight="32">
+
<path
android:fillColor="@color/letterbox_education_text_secondary"
- android:fillType="evenOdd"
- android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
- <path
- android:fillColor="@color/letterbox_education_text_secondary"
- android:pathData="M6 16C6 14.8954 6.89543 14 8 14H21C22.1046 14 23 14.8954 23 16V32C23 33.1046 22.1046 34 21 34H8C6.89543 34 6 33.1046 6 32V16Z" />
- <path
- android:fillColor="@color/letterbox_education_text_secondary"
- android:pathData="M25 16C25 14.8954 25.8954 14 27 14H40C41.1046 14 42 14.8954 42 16V32C42 33.1046 41.1046 34 40 34H27C25.8954 34 25 33.1046 25 32V16Z" />
+ android:pathData="M40 28L40 4C40 1.8 38.2 -7.86805e-08 36 -1.74846e-07L26 -6.11959e-07C23.8 -7.08124e-07 22 1.8 22 4L22 28C22 30.2 23.8 32 26 32L36 32C38.2 32 40 30.2 40 28ZM14 28L4 28L4 4L14 4L14 28ZM18 28L18 4C18 1.8 16.2 -1.04033e-06 14 -1.1365e-06L4 -1.57361e-06C1.8 -1.66978e-06 -7.86805e-08 1.8 -1.74846e-07 4L-1.22392e-06 28C-1.32008e-06 30.2 1.8 32 4 32L14 32C16.2 32 18 30.2 18 28Z" />
</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
index cd1d99a..c65f24d 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
@@ -26,7 +26,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:layout_marginBottom="12dp"/>
+ android:layout_marginBottom="20dp"/>
<TextView
android:id="@+id/letterbox_education_dialog_action_text"
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
index 9592376..3a44eb9 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
@@ -50,13 +50,16 @@
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
- android:padding="24dp">
+ android:paddingTop="32dp"
+ android:paddingBottom="32dp"
+ android:paddingLeft="56dp"
+ android:paddingRight="56dp">
<ImageView
- android:layout_width="@dimen/letterbox_education_dialog_icon_size"
- android:layout_height="@dimen/letterbox_education_dialog_icon_size"
- android:layout_marginBottom="12dp"
- android:src="@drawable/letterbox_education_ic_letterboxed_app"/>
+ android:layout_width="@dimen/letterbox_education_dialog_title_icon_width"
+ android:layout_height="@dimen/letterbox_education_dialog_title_icon_height"
+ android:layout_marginBottom="17dp"
+ android:src="@drawable/letterbox_education_ic_light_bulb"/>
<TextView
android:id="@+id/letterbox_education_dialog_title"
@@ -68,16 +71,6 @@
android:textColor="@color/compat_controls_text"
android:textSize="24sp"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:lineSpacingExtra="4sp"
- android:text="@string/letterbox_education_dialog_subtext"
- android:textAlignment="center"
- android:textColor="@color/letterbox_education_text_secondary"
- android:textSize="14sp"/>
-
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -88,16 +81,16 @@
<com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogActionLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:icon="@drawable/letterbox_education_ic_screen_rotation"
- app:text="@string/letterbox_education_screen_rotation_text"/>
+ app:icon="@drawable/letterbox_education_ic_reposition"
+ app:text="@string/letterbox_education_reposition_text"/>
<com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogActionLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart=
"@dimen/letterbox_education_dialog_space_between_actions"
- app:icon="@drawable/letterbox_education_ic_reposition"
- app:text="@string/letterbox_education_reposition_text"/>
+ app:icon="@drawable/letterbox_education_ic_split_screen"
+ app:text="@string/letterbox_education_split_screen_text"/>
</LinearLayout>
@@ -105,7 +98,7 @@
android:id="@+id/letterbox_education_dialog_dismiss_button"
android:layout_width="match_parent"
android:layout_height="56dp"
- android:layout_marginTop="48dp"
+ android:layout_marginTop="40dp"
android:background=
"@drawable/letterbox_education_dismiss_button_background_ripple"
android:text="@string/letterbox_education_got_it"
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1dac9ca..d2dd8d6b 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -246,8 +246,17 @@
<!-- The corner radius of the letterbox education dialog. -->
<dimen name="letterbox_education_dialog_corner_radius">28dp</dimen>
- <!-- The size of an icon in the letterbox education dialog. -->
- <dimen name="letterbox_education_dialog_icon_size">48dp</dimen>
+ <!-- The width of the top icon in the letterbox education dialog. -->
+ <dimen name="letterbox_education_dialog_title_icon_width">45dp</dimen>
+
+ <!-- The height of the top icon in the letterbox education dialog. -->
+ <dimen name="letterbox_education_dialog_title_icon_height">44dp</dimen>
+
+ <!-- The width of an icon in the letterbox education dialog. -->
+ <dimen name="letterbox_education_dialog_icon_width">40dp</dimen>
+
+ <!-- The height of an icon in the letterbox education dialog. -->
+ <dimen name="letterbox_education_dialog_icon_height">32dp</dimen>
<!-- The fixed width of the dialog if there is enough space in the parent. -->
<dimen name="letterbox_education_dialog_width">472dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 679bfb9..b48a508 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -177,16 +177,13 @@
<string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
<!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] -->
- <string name="letterbox_education_dialog_title">Some apps work best in portrait</string>
+ <string name="letterbox_education_dialog_title">See and do more</string>
- <!-- The subtext of the letterbox education dialog. [CHAR LIMIT=NONE] -->
- <string name="letterbox_education_dialog_subtext">Try one of these options to make the most of your space</string>
-
- <!-- Description of the rotate screen action. [CHAR LIMIT=NONE] -->
- <string name="letterbox_education_screen_rotation_text">Rotate your device to go full screen</string>
+ <!-- Description of the split screen action. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_education_split_screen_text">Drag in another app for split-screen</string>
<!-- Description of the reposition app action. [CHAR LIMIT=NONE] -->
- <string name="letterbox_education_reposition_text">Double-tap next to an app to reposition it</string>
+ <string name="letterbox_education_reposition_text">Double-tap outside an app to reposition it</string>
<!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
<string name="letterbox_education_got_it">Got it</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index dcbb272..d63c25d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1387,9 +1387,6 @@
if (update.selectionChanged && mStackView != null) {
mStackView.setSelectedBubble(update.selectedBubble);
- if (update.selectedBubble != null) {
- mSysuiProxy.updateNotificationSuppression(update.selectedBubble.getKey());
- }
}
// Expanding? Apply this last.
@@ -1448,7 +1445,6 @@
// in the shade, it is essentially removed.
Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey());
if (bubbleChild != null) {
- mSysuiProxy.removeNotificationEntry(bubbleChild.getKey());
bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 0e97e9e..453b34e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -278,12 +278,8 @@
void notifyMaybeCancelSummary(String key);
- void removeNotificationEntry(String key);
-
void updateNotificationBubbleButton(String key);
- void updateNotificationSuppression(String key);
-
void onStackExpandChanged(boolean shouldExpand);
void onManageMenuExpandChanged(boolean menuExpanded);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b7959eb..4c85d20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -80,7 +80,10 @@
public static final int PARALLAX_DISMISSING = 1;
public static final int PARALLAX_ALIGN_CENTER = 2;
- private static final int FLING_ANIMATION_DURATION = 250;
+ private static final int FLING_RESIZE_DURATION = 250;
+ private static final int FLING_SWITCH_DURATION = 350;
+ private static final int FLING_ENTER_DURATION = 350;
+ private static final int FLING_EXIT_DURATION = 350;
private final int mDividerWindowWidth;
private final int mDividerInsets;
@@ -93,6 +96,9 @@
private final Rect mBounds1 = new Rect();
// Bounds2 final position should be always at bottom or right
private final Rect mBounds2 = new Rect();
+ // The temp bounds outside of display bounds for side stage when split screen inactive to avoid
+ // flicker next time active split screen.
+ private final Rect mInvisibleBounds = new Rect();
private final Rect mWinBounds1 = new Rect();
private final Rect mWinBounds2 = new Rect();
private final SplitLayoutHandler mSplitLayoutHandler;
@@ -141,6 +147,8 @@
resetDividerPosition();
mDimNonImeSide = resources.getBoolean(R.bool.config_dimNonImeAttachedSide);
+
+ updateInvisibleRect();
}
private int getDividerInsets(Resources resources, Display display) {
@@ -239,6 +247,12 @@
rect.offset(-mRootBounds.left, -mRootBounds.top);
}
+ /** Gets bounds size equal to root bounds but outside of screen, used for position side stage
+ * when split inactive to avoid flicker when next time active. */
+ public void getInvisibleBounds(Rect rect) {
+ rect.set(mInvisibleBounds);
+ }
+
/** Returns leash of the current divider bar. */
@Nullable
public SurfaceControl getDividerLeash() {
@@ -258,6 +272,14 @@
: (float) ((mBounds1.bottom + mBounds2.top) / 2f) / mBounds2.bottom));
}
+ private void updateInvisibleRect() {
+ mInvisibleBounds.set(mRootBounds.left, mRootBounds.top,
+ isLandscape() ? mRootBounds.right / 2 : mRootBounds.right,
+ isLandscape() ? mRootBounds.bottom : mRootBounds.bottom / 2);
+ mInvisibleBounds.offset(isLandscape() ? mRootBounds.right : 0,
+ isLandscape() ? 0 : mRootBounds.bottom);
+ }
+
/** Applies new configuration, returns {@code false} if there's no effect to the layout. */
public boolean updateConfiguration(Configuration configuration) {
// Update the split bounds when necessary. Besides root bounds changed, split bounds need to
@@ -283,6 +305,7 @@
mRotation = rotation;
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
initDividerPosition(mTempRect);
+ updateInvisibleRect();
return true;
}
@@ -405,6 +428,13 @@
mFreezeDividerWindow = freezeDividerWindow;
}
+ /** Update current layout as divider put on start or end position. */
+ public void setDividerAtBorder(boolean start) {
+ final int pos = start ? mDividerSnapAlgorithm.getDismissStartTarget().position
+ : mDividerSnapAlgorithm.getDismissEndTarget().position;
+ setDividePosition(pos, false /* applyLayoutChange */);
+ }
+
/**
* Updates bounds with the passing position. Usually used to update recording bounds while
* performing animation or dragging divider bar to resize the splits.
@@ -449,17 +479,17 @@
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
switch (snapTarget.flag) {
case FLAG_DISMISS_START:
- flingDividePosition(currentPosition, snapTarget.position,
+ flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER));
break;
case FLAG_DISMISS_END:
- flingDividePosition(currentPosition, snapTarget.position,
+ flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER));
break;
default:
- flingDividePosition(currentPosition, snapTarget.position,
+ flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> setDividePosition(snapTarget.position, true /* applyLayoutChange */));
break;
}
@@ -514,12 +544,20 @@
public void flingDividerToDismiss(boolean toEnd, int reason) {
final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position
: mDividerSnapAlgorithm.getDismissStartTarget().position;
- flingDividePosition(getDividePosition(), target,
+ flingDividePosition(getDividePosition(), target, FLING_EXIT_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason));
}
+ /** Fling divider from current position to center position. */
+ public void flingDividerToCenter() {
+ final int pos = mDividerSnapAlgorithm.getMiddleTarget().position;
+ flingDividePosition(getDividePosition(), pos, FLING_ENTER_DURATION,
+ () -> setDividePosition(pos, true /* applyLayoutChange */));
+ }
+
@VisibleForTesting
- void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
+ void flingDividePosition(int from, int to, int duration,
+ @Nullable Runnable flingFinishedCallback) {
if (from == to) {
// No animation run, still callback to stop resizing.
mSplitLayoutHandler.onLayoutSizeChanged(this);
@@ -529,7 +567,7 @@
}
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
- .setDuration(FLING_ANIMATION_DURATION);
+ .setDuration(duration);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addUpdateListener(
animation -> updateDivideBounds((int) animation.getAnimatedValue()));
@@ -586,7 +624,7 @@
AnimatorSet set = new AnimatorSet();
set.playTogether(animator1, animator2, animator3);
- set.setDuration(FLING_ANIMATION_DURATION);
+ set.setDuration(FLING_SWITCH_DURATION);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 0d75bc4..f1465f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -105,8 +105,8 @@
state.mTaskInfo = taskInfo;
mTasks.put(taskInfo.taskId, state);
- updateRecentsForVisibleFullscreenTask(taskInfo);
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
+ updateRecentsForVisibleFullscreenTask(taskInfo);
if (shouldShowWindowDecor(taskInfo) && mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
state.mWindowDecoration =
@@ -135,8 +135,8 @@
mWindowDecorViewModelOptional.get().onTaskInfoChanged(
state.mTaskInfo, state.mWindowDecoration);
}
- updateRecentsForVisibleFullscreenTask(taskInfo);
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
+ updateRecentsForVisibleFullscreenTask(taskInfo);
final Point positionInParent = state.mTaskInfo.positionInParent;
if (!oldPositionInParent.equals(state.mTaskInfo.positionInParent)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 7e83d2f..c08aa5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -25,6 +25,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -488,13 +489,6 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
- // If split still not active, apply windows bounds first to avoid surface reset to
- // wrong pos by SurfaceAnimator from wms.
- // TODO(b/223325631): check is it still necessary after improve enter transition done.
- if (!mMainStage.isActive()) {
- updateWindowBounds(mSplitLayout, wct);
- }
-
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
@@ -519,6 +513,7 @@
mSplitLayout.setDivideRatio(splitRatio);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, false);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
@@ -628,6 +623,7 @@
}
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, false);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
@@ -640,8 +636,8 @@
} else {
wct.startTask(sideTaskId, sideOptions);
}
- // Using legacy transitions, so we can't use blast sync since it conflicts.
- mTaskOrganizer.applyTransaction(wct);
+
+ mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t);
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
@@ -893,10 +889,13 @@
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = false;
+ mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null) {
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
+ wct.setForceTranslucent(mRootTaskInfo.token, true);
+ wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
onTransitionAnimationComplete();
} else {
// Expand to top side split as full screen for fading out decor animation and dismiss
@@ -907,27 +906,32 @@
? mSideStage : mMainStage;
tempFullStage.resetBounds(wct);
wct.setSmallestScreenWidthDp(tempFullStage.mRootTaskInfo.token,
- mRootTaskInfo.configuration.smallestScreenWidthDp);
+ SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
dismissStage.dismiss(wct, false /* toTop */);
}
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
t.setWindowCrop(mMainStage.mRootLeash, null)
.setWindowCrop(mSideStage.mRootLeash, null);
- t.setPosition(mMainStage.mRootLeash, 0, 0)
- .setPosition(mSideStage.mRootLeash, 0, 0);
t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
setDividerVisibility(false, t);
- // In this case, exit still under progress, fade out the split decor after first WCT
- // done and do remaining WCT after animation finished.
- if (childrenToTop != null) {
+ if (childrenToTop == null) {
+ t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
+ } else {
+ // In this case, exit still under progress, fade out the split decor after first WCT
+ // done and do remaining WCT after animation finished.
childrenToTop.fadeOutDecor(() -> {
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
childrenToTop.dismiss(finishedWCT, true /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
- mTaskOrganizer.applyTransaction(finishedWCT);
+ finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
+ finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
+ mSyncQueue.queue(finishedWCT);
+ mSyncQueue.runInSync(at -> {
+ at.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
+ });
onTransitionAnimationComplete();
});
}
@@ -996,6 +1000,7 @@
mMainStage.activate(wct, true /* includingTopTask */);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, false);
}
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -1221,7 +1226,13 @@
// Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
- mTaskOrganizer.applyTransaction(wct);
+ wct.setForceTranslucent(mRootTaskInfo.token, true);
+ mSplitLayout.getInvisibleBounds(mTempRect1);
+ wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> {
+ t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top);
+ });
}
private void onRootTaskVanished() {
@@ -1377,10 +1388,15 @@
// TODO (b/238697912) : Add the validation to prevent entering non-recovered status
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
- prepareEnterSplitScreen(wct);
+ mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
+ mMainStage.activate(wct, true /* includingTopTask */);
+ updateWindowBounds(mSplitLayout, wct);
+ wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, false);
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t ->
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
+ mSyncQueue.runInSync(t -> {
+ mSplitLayout.flingDividerToCenter();
+ });
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true;
@@ -1822,6 +1838,7 @@
// properly for the animation itself.
mSplitLayout.release();
mSplitLayout.resetDividerPosition();
+ mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 67d7aca..298bf68 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -86,8 +86,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
}
const val FIND_OBJECT_TIMEOUT = 2000L
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
index 293eb7c..47557bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -18,7 +18,6 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
import android.view.WindowInsets
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
@@ -92,7 +91,7 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 242088970)
@Test
fun testAppIsVisibleAtEnd() {
testSpec.assertLayersEnd {
@@ -125,7 +124,7 @@
super.navBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest(bugId = 242088970)
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() =
super.taskBarLayerIsVisibleAtStartAndEnd()
@@ -135,4 +134,28 @@
@Test
override fun taskBarWindowIsAlwaysVisible() =
super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
index 6695c17..1b8a44b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
@@ -27,7 +27,6 @@
) {
companion object {
- const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index e7f9d9a..87a863d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -42,7 +42,6 @@
) : BaseAppHelper(instrumentation, activityLabel, componentInfo) {
companion object {
- const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
const val DRAG_DURATION_MS = 1_000L
const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index d194472..6fcd17a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -185,8 +185,7 @@
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 507562b..a99439e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -246,8 +246,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index b5a3c78..39a7017 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -100,7 +100,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3)
+ supportedRotations = listOf(Surface.ROTATION_0))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 1d26614..421a6fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -125,7 +125,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3)
+ supportedRotations = listOf(Surface.ROTATION_0))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index b71a9d8..3bffef0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -86,8 +86,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 31a39c1..75d25e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -105,8 +105,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index fd661cf..825aca3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -178,8 +178,7 @@
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index b3f0fb9..d3e2ce15 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -89,7 +89,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 8bd5c54..3d64bbe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -105,7 +105,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 454927e..be39fae 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -116,8 +116,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 09248a1..4dc9858 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -264,8 +264,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
- repetitions = 3
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 6d64cb9..d5de22f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -224,8 +224,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 1)
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index d238814..6cbb685 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -178,7 +178,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index ba40c27..581826e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -195,7 +195,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 6828589..5c051e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -182,7 +182,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 9ac7c23..9ca9ab0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -186,7 +186,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_0),
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 8401c1a..8e2769f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -180,7 +180,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 168afda..531d376 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -195,7 +195,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index c1fce5f..ea43c7e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -183,7 +183,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 8cb5d7c..7ce23c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -170,8 +170,7 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests()
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 1530561..525e09a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -180,7 +180,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 20544bd..c030603 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -184,7 +184,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 5a8604f..b8565f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -183,7 +183,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index adea66a..20d7f2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -185,7 +185,6 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 95725bb..695550d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -159,7 +159,8 @@
}
private void waitDividerFlingFinished() {
- verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture());
+ verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), anyInt(),
+ mRunnableCaptor.capture());
mRunnableCaptor.getValue().run();
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 88f73d6..b11e542 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -93,6 +93,10 @@
cc_defaults {
name: "hwui_static_deps",
+ defaults: [
+ "android.hardware.graphics.common-ndk_shared",
+ "android.hardware.graphics.composer3-ndk_shared",
+ ],
shared_libs: [
"libbase",
"libharfbuzz_ng",
@@ -106,9 +110,7 @@
target: {
android: {
shared_libs: [
- "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.composer3-V1-ndk",
"liblog",
"libcutils",
"libutils",
diff --git a/media/Android.bp b/media/Android.bp
index 7118afa..e97f077 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -35,8 +35,8 @@
"aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
],
imports: [
- "android.media.audio.common.types",
- "android.media.soundtrigger.types",
+ "android.media.audio.common.types-V2",
+ "android.media.soundtrigger.types-V1",
"media_permission-aidl",
],
}
@@ -232,12 +232,12 @@
},
},
imports: [
- "android.media.audio.common.types",
+ "android.media.audio.common.types-V2",
],
versions_with_info: [
{
version: "1",
- imports: ["android.media.audio.common.types-V1"],
+ imports: ["android.media.audio.common.types-V2"],
},
],
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index c708876..40f6dc5 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -21,6 +21,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -39,23 +41,29 @@
* @hide
* AudioDeviceVolumeManager provides access to audio device volume control.
*/
+@SystemApi
public class AudioDeviceVolumeManager {
// define when using Log.*
//private static final String TAG = "AudioDeviceVolumeManager";
- /** Indicates no special treatment in the handling of the volume adjustment */
+ /** @hide
+ * Indicates no special treatment in the handling of the volume adjustment */
public static final int ADJUST_MODE_NORMAL = 0;
- /** Indicates the start of a volume adjustment */
+ /** @hide
+ * Indicates the start of a volume adjustment */
public static final int ADJUST_MODE_START = 1;
- /** Indicates the end of a volume adjustment */
+ /** @hide
+ * Indicates the end of a volume adjustment */
public static final int ADJUST_MODE_END = 2;
+ /** @hide */
@IntDef(flag = false, prefix = "ADJUST_MODE", value = {
ADJUST_MODE_NORMAL,
ADJUST_MODE_START,
ADJUST_MODE_END}
)
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
public @interface VolumeAdjustmentMode {}
@@ -64,7 +72,16 @@
private final @NonNull String mPackageName;
private final @Nullable String mAttributionTag;
- public AudioDeviceVolumeManager(Context context) {
+ /**
+ * Constructor
+ * @param context the Context for the device volume operations
+ */
+ @SuppressLint("ManagerConstructor")
+ // reason for suppression: even though the functionality handled by this class is implemented in
+ // AudioService, we want to avoid bloating android.media.AudioManager
+ // with @SystemApi functionality
+ public AudioDeviceVolumeManager(@NonNull Context context) {
+ Objects.requireNonNull(context);
mPackageName = context.getApplicationContext().getOpPackageName();
mAttributionTag = context.getApplicationContext().getAttributionTag();
}
@@ -101,6 +118,7 @@
@VolumeAdjustmentMode int mode);
}
+ /** @hide */
static class ListenerInfo {
final @NonNull OnAudioDeviceVolumeChangedListener mListener;
final @NonNull Executor mExecutor;
@@ -127,6 +145,7 @@
@GuardedBy("mDeviceVolumeListenerLock")
private DeviceVolumeDispatcherStub mDeviceVolumeDispatcherStub;
+ /** @hide */
final class DeviceVolumeDispatcherStub extends IAudioDeviceVolumeDispatcher.Stub {
/**
* Register / unregister the stub
@@ -305,6 +324,7 @@
* @param vi the volume information, only stream-based volumes are supported
* @param ada the device for which volume is to be modified
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada) {
try {
@@ -315,6 +335,7 @@
}
/**
+ * @hide
* Return human-readable name for volume behavior
* @param behavior one of the volume behaviors defined in AudioManager
* @return a string for the given behavior
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 650f360..72190e3 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -65,8 +65,6 @@
private static final String TAG = "AudioSystem";
- private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
-
// private constructor to prevent instantiating AudioSystem
private AudioSystem() {
throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
@@ -293,7 +291,7 @@
case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
- case AUDIO_FORMAT_OPUS: return SOURCE_CODEC_TYPE_OPUS; // TODO update in U
+ case AUDIO_FORMAT_OPUS: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS;
default:
Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
+ " for conversion to BT codec");
@@ -336,7 +334,7 @@
return AudioSystem.AUDIO_FORMAT_LDAC;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
return AudioSystem.AUDIO_FORMAT_LC3;
- case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS:
return AudioSystem.AUDIO_FORMAT_OPUS;
default:
Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ee2e448..6317320 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -5092,9 +5092,12 @@
@Nullable Map<String, String> optionalParameters)
throws NoDrmSchemeException
{
- Log.v(TAG, "getKeyRequest: " +
- " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
- " keyType: " + keyType + " optionalParameters: " + optionalParameters);
+ Log.v(TAG, "getKeyRequest: "
+ + " keySetId: " + Arrays.toString(keySetId)
+ + " initData:" + Arrays.toString(initData)
+ + " mimeType: " + mimeType
+ + " keyType: " + keyType
+ + " optionalParameters: " + optionalParameters);
synchronized (mDrmLock) {
if (!mActiveDrmScheme) {
@@ -5153,7 +5156,8 @@
public byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
throws NoDrmSchemeException, DeniedByServerException
{
- Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response);
+ Log.v(TAG, "provideKeyResponse: keySetId: " + Arrays.toString(keySetId)
+ + " response: " + Arrays.toString(response));
synchronized (mDrmLock) {
@@ -5169,8 +5173,9 @@
byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
- Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response +
- " --> " + keySetResult);
+ Log.v(TAG, "provideKeyResponse: keySetId: " + Arrays.toString(keySetId)
+ + " response: " + Arrays.toString(response)
+ + " --> " + Arrays.toString(keySetResult));
return keySetResult;
@@ -5197,7 +5202,7 @@
public void restoreKeys(@NonNull byte[] keySetId)
throws NoDrmSchemeException
{
- Log.v(TAG, "restoreKeys: keySetId: " + keySetId);
+ Log.v(TAG, "restoreKeys: keySetId: " + Arrays.toString(keySetId));
synchronized (mDrmLock) {
@@ -5484,7 +5489,8 @@
// at prepareDrm/openSession rather than getKeyRequest/provideKeyResponse
try {
mDrmSessionId = mDrmObj.openSession();
- Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
+ Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId="
+ + Arrays.toString(mDrmSessionId));
// Sending it down to native/mediaserver to create the crypto object
// This call could simply fail due to bad player state, e.g., after start().
@@ -5547,7 +5553,7 @@
response = Streams.readFully(connection.getInputStream());
Log.v(TAG, "HandleProvisioninig: Thread run: response " +
- response.length + " " + response);
+ response.length + " " + Arrays.toString(response));
} catch (Exception e) {
status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url);
@@ -5631,8 +5637,9 @@
return PREPARE_DRM_STATUS_PREPARATION_ERROR;
}
- Log.v(TAG, "HandleProvisioninig provReq " +
- " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
+ Log.v(TAG, "HandleProvisioninig provReq "
+ + " data: " + Arrays.toString(provReq.getData())
+ + " url: " + provReq.getDefaultUrl());
// networking in a background thread
mDrmProvisioningInProgress = true;
@@ -5715,7 +5722,8 @@
private void cleanDrmObj()
{
// the caller holds mDrmLock
- Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
+ Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj
+ + " mDrmSessionId=" + Arrays.toString(mDrmSessionId));
if (mDrmSessionId != null) {
mDrmObj.closeSession(mDrmSessionId);
diff --git a/media/java/android/media/VolumeInfo.java b/media/java/android/media/VolumeInfo.java
index c61b0e5..6b4f604 100644
--- a/media/java/android/media/VolumeInfo.java
+++ b/media/java/android/media/VolumeInfo.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.Context;
import android.media.audiopolicy.AudioVolumeGroup;
import android.os.IBinder;
@@ -32,16 +33,13 @@
/**
* @hide
- * A class to represent type of volume information.
+ * A class to represent volume information.
* Can be used to represent volume associated with a stream type or {@link AudioVolumeGroup}.
* Volume index is optional when used to represent a category of volume.
* Index ranges are supported too, making the representation of volume changes agnostic to the
* range (e.g. can be used to map BT A2DP absolute volume range to internal range).
- *
- * Note: this class is not yet part of the SystemApi but is intended to be gradually introduced
- * particularly in parts of the audio framework that suffer from code ambiguity when
- * dealing with different volume ranges / units.
*/
+@SystemApi
public final class VolumeInfo implements Parcelable {
private static final String TAG = "VolumeInfo";
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 31d5967..f957498 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -32,6 +32,7 @@
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -182,7 +183,7 @@
AudioProductStrategy thatStrategy = (AudioProductStrategy) o;
return mName == thatStrategy.mName && mId == thatStrategy.mId
- && mAudioAttributesGroups.equals(thatStrategy.mAudioAttributesGroups);
+ && Arrays.equals(mAudioAttributesGroups, thatStrategy.mAudioAttributesGroups);
}
/**
@@ -415,7 +416,7 @@
return mVolumeGroupId == thatAag.mVolumeGroupId
&& mLegacyStreamType == thatAag.mLegacyStreamType
- && mAudioAttributes.equals(thatAag.mAudioAttributes);
+ && Arrays.equals(mAudioAttributes, thatAag.mAudioAttributes);
}
public int getStreamType() {
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
index 79be922..d58111d 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroup.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
@@ -114,7 +114,7 @@
AudioVolumeGroup thatAvg = (AudioVolumeGroup) o;
return mName == thatAvg.mName && mId == thatAvg.mId
- && mAudioAttributes.equals(thatAvg.mAudioAttributes);
+ && Arrays.equals(mAudioAttributes, thatAvg.mAudioAttributes);
}
/**
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index e71ee20..51a2c9d 100644
--- a/media/java/android/media/metrics/PlaybackMetrics.java
+++ b/media/java/android/media/metrics/PlaybackMetrics.java
@@ -402,9 +402,10 @@
@Override
public int hashCode() {
return Objects.hash(mMediaDurationMillis, mStreamSource, mStreamType, mPlaybackType,
- mDrmType, mContentType, mPlayerName, mPlayerVersion, mExperimentIds,
- mVideoFramesPlayed, mVideoFramesDropped, mAudioUnderrunCount, mNetworkBytesRead,
- mLocalBytesRead, mNetworkTransferDurationMillis, mDrmSessionId);
+ mDrmType, mContentType, mPlayerName, mPlayerVersion,
+ Arrays.hashCode(mExperimentIds), mVideoFramesPlayed, mVideoFramesDropped,
+ mAudioUnderrunCount, mNetworkBytesRead, mLocalBytesRead,
+ mNetworkTransferDurationMillis, Arrays.hashCode(mDrmSessionId));
}
@Override
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 31fb8d0..9bf126b 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
- void setMediaButtonReceiver(in PendingIntent mbr);
+ void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
void setLaunchPendingIntent(in PendingIntent pi);
void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1bd12af..9e265d8 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@
@Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
- mBinder.setMediaButtonReceiver(mbr);
+ mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java
index 41914b8..ebf1a75 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java
@@ -16,10 +16,6 @@
package com.android.mediaframeworktest.helpers;
-import org.hamcrest.CoreMatchers;
-import org.hamcrest.Matcher;
-import org.junit.rules.ErrorCollector;
-
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
@@ -30,6 +26,10 @@
import android.util.Log;
import android.util.Size;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matcher;
+import org.junit.rules.ErrorCollector;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -903,7 +903,7 @@
if ((value = expectKeyValueNotNull(characteristics, key)) == null) {
return;
}
- String reason = "Key " + key.getName() + " value " + value
+ String reason = "Key " + key.getName() + " value " + Arrays.toString(value)
+ " doesn't contain the expected value " + expected;
expectContains(reason, value, expected);
}
@@ -921,7 +921,7 @@
if ((value = expectKeyValueNotNull(characteristics, key)) == null) {
return;
}
- String reason = "Key " + key.getName() + " value " + value
+ String reason = "Key " + key.getName() + " value " + Arrays.toString(value)
+ " doesn't contain the expected value " + expected;
expectContains(reason, value, expected);
}
@@ -939,7 +939,7 @@
if ((value = expectKeyValueNotNull(characteristics, key)) == null) {
return;
}
- String reason = "Key " + key.getName() + " value " + value
+ String reason = "Key " + key.getName() + " value " + Arrays.toString(value)
+ " doesn't contain the expected value " + expected;
expectContains(reason, value, expected);
}
@@ -960,7 +960,7 @@
public <T> void expectContains(T[] values, T expected) {
String reason = "Expected value " + expected
- + " is not contained in the given values " + values;
+ + " is not contained in the given values " + Arrays.toString(values);
expectContains(reason, values, expected);
}
@@ -996,7 +996,7 @@
public void expectContains(int[] values, int expected) {
String reason = "Expected value " + expected
- + " is not contained in the given values " + values;
+ + " is not contained in the given values " + Arrays.toString(values);
expectContains(reason, values, expected);
}
@@ -1040,7 +1040,7 @@
*/
public void expectContains(boolean[] values, boolean expected) {
String reason = "Expected value " + expected
- + " is not contained in the given values " + values;
+ + " is not contained in the given values " + Arrays.toString(values);
expectContains(reason, values, expected);
}
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 7fe2735..cd6ed23 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -381,7 +381,7 @@
SkIRect cropIRect;
cropIRect.setLTRB(crop.left, crop.top, crop.right, crop.bottom);
- SkIRect* cropPtr = cropIRect.isEmpty() ? nullptr : &cropIRect;
+ SkIRect* cropPtr = cropIRect == SkIRect::MakeEmpty() ? nullptr : &cropIRect;
return imageDecoder->setCropRect(cropPtr)
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
diff --git a/opengl/java/android/opengl/GLLogWrapper.java b/opengl/java/android/opengl/GLLogWrapper.java
index bff7396..e645afa 100644
--- a/opengl/java/android/opengl/GLLogWrapper.java
+++ b/opengl/java/android/opengl/GLLogWrapper.java
@@ -2812,7 +2812,7 @@
public void glDeleteBuffers(int n, int[] buffers, int offset) {
begin("glDeleteBuffers");
arg("n", n);
- arg("buffers", buffers.toString());
+ arg("buffers", Arrays.toString(buffers));
arg("offset", offset);
end();
mgl11.glDeleteBuffers(n, buffers, offset);
@@ -2831,7 +2831,7 @@
public void glGenBuffers(int n, int[] buffers, int offset) {
begin("glGenBuffers");
arg("n", n);
- arg("buffers", buffers.toString());
+ arg("buffers", Arrays.toString(buffers));
arg("offset", offset);
end();
mgl11.glGenBuffers(n, buffers, offset);
@@ -2850,7 +2850,7 @@
public void glGetBooleanv(int pname, boolean[] params, int offset) {
begin("glGetBooleanv");
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetBooleanv(pname, params, offset);
@@ -2871,7 +2871,7 @@
begin("glGetBufferParameteriv");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetBufferParameteriv(target, pname, params, offset);
@@ -2891,7 +2891,7 @@
public void glGetClipPlanef(int pname, float[] eqn, int offset) {
begin("glGetClipPlanef");
arg("pname", pname);
- arg("eqn", eqn.toString());
+ arg("eqn", Arrays.toString(eqn));
arg("offset", offset);
end();
mgl11.glGetClipPlanef(pname, eqn, offset);
@@ -2910,7 +2910,7 @@
public void glGetClipPlanex(int pname, int[] eqn, int offset) {
begin("glGetClipPlanex");
arg("pname", pname);
- arg("eqn", eqn.toString());
+ arg("eqn", Arrays.toString(eqn));
arg("offset", offset);
end();
mgl11.glGetClipPlanex(pname, eqn, offset);
@@ -2928,7 +2928,7 @@
public void glGetFixedv(int pname, int[] params, int offset) {
begin("glGetFixedv");
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetFixedv(pname, params, offset);
@@ -2946,7 +2946,7 @@
public void glGetFloatv(int pname, float[] params, int offset) {
begin("glGetFloatv");
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetFloatv(pname, params, offset);
@@ -2965,7 +2965,7 @@
begin("glGetLightfv");
arg("light", light);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetLightfv(light, pname, params, offset);
@@ -2986,7 +2986,7 @@
begin("glGetLightxv");
arg("light", light);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetLightxv(light, pname, params, offset);
@@ -3008,7 +3008,7 @@
begin("glGetMaterialfv");
arg("face", face);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetMaterialfv(face, pname, params, offset);
@@ -3029,7 +3029,7 @@
begin("glGetMaterialxv");
arg("face", face);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetMaterialxv(face, pname, params, offset);
@@ -3050,7 +3050,7 @@
begin("glGetTexEnviv");
arg("env", env);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetTexEnviv(env, pname, params, offset);
@@ -3071,7 +3071,7 @@
begin("glGetTexEnviv");
arg("env", env);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetTexEnviv(env, pname, params, offset);
@@ -3092,7 +3092,7 @@
begin("glGetTexParameterfv");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetTexParameterfv(target, pname, params, offset);
@@ -3113,7 +3113,7 @@
begin("glGetTexParameteriv");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetTexEnviv(target, pname, params, offset);
@@ -3135,7 +3135,7 @@
begin("glGetTexParameterxv");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glGetTexParameterxv(target, pname, params, offset);
@@ -3191,7 +3191,7 @@
public void glPointParameterfv(int pname, float[] params, int offset) {
begin("glPointParameterfv");
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glPointParameterfv(pname, params, offset);
@@ -3219,7 +3219,7 @@
public void glPointParameterxv(int pname, int[] params, int offset) {
begin("glPointParameterxv");
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glPointParameterxv(pname, params, offset);
@@ -3259,7 +3259,7 @@
begin("glTexEnviv");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glTexEnviv(target, pname, params, offset);
@@ -3281,7 +3281,7 @@
begin("glTexParameterfv");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glTexParameterfv( target, pname, params, offset);
@@ -3313,7 +3313,7 @@
begin("glTexParameterxv");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11.glTexParameterxv(target, pname, params, offset);
@@ -3356,7 +3356,7 @@
public void glGetPointerv(int pname, Buffer[] params) {
begin("glGetPointerv");
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
end();
mgl11.glGetPointerv(pname, params);
checkError();
@@ -3513,7 +3513,7 @@
public void glDeleteFramebuffersOES(int n, int[] framebuffers, int offset) {
begin("glDeleteFramebuffersOES");
arg("n", n);
- arg("framebuffers", framebuffers.toString());
+ arg("framebuffers", Arrays.toString(framebuffers));
arg("offset", offset);
end();
mgl11ExtensionPack.glDeleteFramebuffersOES(n, framebuffers, offset);
@@ -3534,7 +3534,7 @@
public void glDeleteRenderbuffersOES(int n, int[] renderbuffers, int offset) {
begin("glDeleteRenderbuffersOES");
arg("n", n);
- arg("renderbuffers", renderbuffers.toString());
+ arg("renderbuffers", Arrays.toString(renderbuffers));
arg("offset", offset);
end();
mgl11ExtensionPack.glDeleteRenderbuffersOES(n, renderbuffers, offset);
@@ -3591,7 +3591,7 @@
public void glGenFramebuffersOES(int n, int[] framebuffers, int offset) {
begin("glGenFramebuffersOES");
arg("n", n);
- arg("framebuffers", framebuffers.toString());
+ arg("framebuffers", Arrays.toString(framebuffers));
arg("offset", offset);
end();
mgl11ExtensionPack.glGenFramebuffersOES(n, framebuffers, offset);
@@ -3612,7 +3612,7 @@
public void glGenRenderbuffersOES(int n, int[] renderbuffers, int offset) {
begin("glGenRenderbuffersOES");
arg("n", n);
- arg("renderbuffers", renderbuffers.toString());
+ arg("renderbuffers", Arrays.toString(renderbuffers));
arg("offset", offset);
end();
mgl11ExtensionPack.glGenRenderbuffersOES(n, renderbuffers, offset);
@@ -3636,7 +3636,7 @@
arg("target", target);
arg("attachment", attachment);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glGetFramebufferAttachmentParameterivOES(target, attachment, pname, params, offset);
@@ -3662,7 +3662,7 @@
begin("glGetRenderbufferParameterivOES");
arg("target", target);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glGetRenderbufferParameterivOES(target, pname, params, offset);
@@ -3686,7 +3686,7 @@
begin("glGetTexGenfv");
arg("coord", coord);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glGetTexGenfv(coord, pname, params, offset);
@@ -3709,7 +3709,7 @@
begin("glGetTexGeniv");
arg("coord", coord);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glGetTexGeniv(coord, pname, params, offset);
@@ -3732,7 +3732,7 @@
begin("glGetTexGenxv");
arg("coord", coord);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glGetTexGenxv(coord, pname, params, offset);
@@ -3799,7 +3799,7 @@
begin("glTexGenfv");
arg("coord", coord);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glTexGenfv(coord, pname, params, offset);
@@ -3833,7 +3833,7 @@
begin("glTexGeniv");
arg("coord", coord);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glTexGeniv(coord, pname, params, offset);
@@ -3867,7 +3867,7 @@
begin("glTexGenxv");
arg("coord", coord);
arg("pname", pname);
- arg("params", params.toString());
+ arg("params", Arrays.toString(params));
arg("offset", offset);
end();
mgl11ExtensionPack.glTexGenxv(coord, pname, params, offset);
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_info.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_info.xml
index 5689e34..a984938 100644
--- a/packages/CompanionDeviceManager/res/drawable-night/ic_info.xml
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_info.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="@android:color/system_accent1_200">
+ android:tint="@android:color/system_neutral1_200">
<path android:fillColor="@android:color/white"
android:pathData="M11,17H13V11H11ZM12,9Q12.425,9 12.713,8.712Q13,8.425 13,8Q13,7.575 12.713,7.287Q12.425,7 12,7Q11.575,7 11.288,7.287Q11,7.575 11,8Q11,8.425 11.288,8.712Q11.575,9 12,9ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12ZM12,20Q15.325,20 17.663,17.663Q20,15.325 20,12Q20,8.675 17.663,6.337Q15.325,4 12,4Q8.675,4 6.338,6.337Q4,8.675 4,12Q4,15.325 6.338,17.663Q8.675,20 12,20Z"/>
</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_info.xml b/packages/CompanionDeviceManager/res/drawable/ic_info.xml
index d9f6a27..229960c 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_info.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_info.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="@android:color/system_accent1_600">
+ android:tint="@android:color/system_neutral1_700">
<path android:fillColor="@android:color/white"
android:pathData="M11,17H13V11H11ZM12,9Q12.425,9 12.713,8.712Q13,8.425 13,8Q13,7.575 12.713,7.287Q12.425,7 12,7Q11.575,7 11.288,7.287Q11,7.575 11,8Q11,8.425 11.288,8.712Q11.575,9 12,9ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12ZM12,20Q15.325,20 17.663,17.663Q20,15.325 20,12Q20,8.675 17.663,6.337Q15.325,4 12,4Q8.675,4 6.338,6.337Q4,8.675 4,12Q4,15.325 6.338,17.663Q8.675,20 12,20Z"/>
</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
index d0d46f7..1f922b9 100644
--- a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
@@ -35,7 +35,7 @@
<ImageView
android:id="@+id/app_icon"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="32dp"
android:gravity="center"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"/>
diff --git a/packages/CompanionDeviceManager/res/layout/vendor_header.xml b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
index 14e7431..16a87e5 100644
--- a/packages/CompanionDeviceManager/res/layout/vendor_header.xml
+++ b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center"
- android:paddingTop="24dp"
+ android:paddingTop="12dp"
android:paddingBottom="4dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
@@ -32,24 +32,26 @@
android:id="@+id/vendor_header_image"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:padding="12dp"
android:contentDescription="@string/vendor_icon_description" />
<TextView
android:id="@+id/vendor_header_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
- android:textSize="16sp"
+ android:textSize="14sp"
android:layout_toEndOf="@+id/vendor_header_image"
android:textColor="?android:attr/textColorSecondary"/>
<ImageButton
android:id="@+id/vendor_header_button"
- android:background="@drawable/ic_info"
+ android:src="@drawable/ic_info"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:padding="12dp"
android:contentDescription="@string/vendor_header_button_description"
- android:layout_alignParentEnd="true" />
+ android:layout_alignParentEnd="true"
+ android:background="@android:color/transparent" />
</RelativeLayout>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index 2000d96..428f2dc 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -49,6 +49,7 @@
<style name="DescriptionSummary">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
<item name="android:layout_marginTop">18dp</item>
<item name="android:layout_marginLeft">18dp</item>
<item name="android:layout_marginRight">18dp</item>
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index d380136..5dc2aa0 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-beta04' apply false
- id 'com.android.library' version '7.3.0-beta04' apply false
+ id 'com.android.application' version '7.3.0-rc01' apply false
+ id 'com.android.library' version '7.3.0-rc01' 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/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
index e300624..3233d4d 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/SpaEnvironment.kt
@@ -20,18 +20,22 @@
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
-import com.android.settingslib.spa.gallery.page.PreferencePageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
-import com.android.settingslib.spa.gallery.page.SwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
+import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
val galleryPageProviders = SettingsPageProviderRepository(
allPagesList = listOf(
HomePageProvider,
+ PreferenceMainPageProvider,
PreferencePageProvider,
SwitchPreferencePageProvider,
ArgumentPageProvider,
SliderPageProvider,
+ SpinnerPageProvider,
SettingsPagerPageProvider,
FooterPageProvider,
),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index a85ee2a..fdcb1c2 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -25,10 +25,10 @@
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
-import com.android.settingslib.spa.gallery.page.PreferencePageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
-import com.android.settingslib.spa.gallery.page.SwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
object HomePageProvider : SettingsPageProvider {
@@ -43,11 +43,11 @@
@Composable
private fun HomePage() {
HomeScaffold(title = stringResource(R.string.app_name)) {
- PreferencePageProvider.EntryItem()
- SwitchPreferencePageProvider.EntryItem()
+ PreferenceMainPageProvider.EntryItem()
ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = 0)
SliderPageProvider.EntryItem()
+ SpinnerPageProvider.EntryItem()
SettingsPagerPageProvider.EntryItem()
FooterPageProvider.EntryItem()
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
index 3146351..130cbd9 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
@@ -30,11 +30,11 @@
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.SettingsSlider
+import com.android.settingslib.spa.widget.SettingsSliderModel
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
-import com.android.settingslib.spa.widget.ui.SettingsSlider
-import com.android.settingslib.spa.widget.ui.SettingsSliderModel
private const val TITLE = "Sample Slider"
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
new file mode 100644
index 0000000..d3ca1d3
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Category: Preference"
+
+object PreferenceMainPageProvider : SettingsPageProvider {
+ override val name = "PreferenceMain"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ PreferenceMain()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+}
+
+@Composable
+private fun PreferenceMain() {
+ RegularScaffold(title = TITLE) {
+ PreferencePageProvider.EntryItem()
+ SwitchPreferencePageProvider.EntryItem()
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
similarity index 98%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
index 90dacdb..36c619f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery.page
+package com.android.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.material.icons.Icons
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
similarity index 98%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
index 6d5d448..8be6a89 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery.page
+package com.android.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.runtime.Composable
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt
new file mode 100644
index 0000000..7efa85b
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.gallery.ui
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.Spinner
+
+private const val TITLE = "Sample Spinner"
+
+object SpinnerPageProvider : SettingsPageProvider {
+ override val name = "Spinner"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ SpinnerPage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+}
+
+@Composable
+private fun SpinnerPage() {
+ RegularScaffold(title = TITLE) {
+ val selectedIndex = rememberSaveable { mutableStateOf(0) }
+ Spinner(
+ options = (1..3).map { "Option $it" },
+ selectedIndex = selectedIndex.value,
+ setIndex = { selectedIndex.value = it },
+ )
+ Preference(object : PreferenceModel {
+ override val title = "Selected index"
+ override val summary = remember { derivedStateOf { selectedIndex.value.toString() } }
+ })
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SpinnerPagePreview() {
+ SettingsTheme {
+ SpinnerPage()
+ }
+}
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 6a7b17a..e8d1ea2 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
@@ -21,6 +21,8 @@
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@@ -46,12 +48,11 @@
@Composable
private fun MainContent() {
- val startDestination =
- intent?.getStringExtra(KEY_START_DESTINATION) ?: sppRepository.getDefaultStartPageName()
+ val destination = intent?.getStringExtra(KEY_DESTINATION)
val navController = rememberNavController()
CompositionLocalProvider(navController.localNavController()) {
- NavHost(navController, startDestination) {
+ NavHost(navController, sppRepository.getDefaultStartPageName()) {
for (page in sppRepository.getAllProviders()) {
composable(
route = page.route,
@@ -61,6 +62,16 @@
}
}
}
+
+ if (!destination.isNullOrEmpty()) {
+ LaunchedEffect(Unit) {
+ navController.navigate(destination) {
+ popUpTo(navController.graph.findStartDestination().id) {
+ inclusive = true
+ }
+ }
+ }
+ }
}
}
@@ -68,6 +79,6 @@
get() = name + arguments.joinToString("") { argument -> "/{${argument.name}}" }
companion object {
- const val KEY_START_DESTINATION = "spa:SpaActivity:startDestination"
+ const val KEY_DESTINATION = "spa:SpaActivity:destination"
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
index c68d5de..32ef0bb 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
@@ -16,25 +16,35 @@
package com.android.settingslib.spa.framework.compose
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ProvidedValue
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.navigation.NavHostController
interface NavControllerWrapper {
fun navigate(route: String)
- fun navigateUp()
+ fun navigateBack()
}
@Composable
-fun NavHostController.localNavController() =
- LocalNavController provides remember { NavControllerWrapperImpl(this) }
+fun NavHostController.localNavController(): ProvidedValue<NavControllerWrapper> {
+ val onBackPressedDispatcherOwner = LocalOnBackPressedDispatcherOwner.current
+ return LocalNavController provides remember {
+ NavControllerWrapperImpl(
+ navController = this,
+ onBackPressedDispatcher = onBackPressedDispatcherOwner?.onBackPressedDispatcher,
+ )
+ }
+}
val LocalNavController = compositionLocalOf<NavControllerWrapper> {
object : NavControllerWrapper {
override fun navigate(route: String) {}
- override fun navigateUp() {}
+ override fun navigateBack() {}
}
}
@@ -46,12 +56,13 @@
internal class NavControllerWrapperImpl(
private val navController: NavHostController,
+ private val onBackPressedDispatcher: OnBackPressedDispatcher?,
) : NavControllerWrapper {
override fun navigate(route: String) {
navController.navigate(route)
}
- override fun navigateUp() {
- navController.navigateUp()
+ override fun navigateBack() {
+ onBackPressedDispatcher?.onBackPressed()
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
index 27fdc91..bc316f7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
@@ -31,6 +31,10 @@
val secondaryText: Color = Color.Unspecified,
val primaryContainer: Color = Color.Unspecified,
val onPrimaryContainer: Color = Color.Unspecified,
+ val spinnerHeaderContainer: Color = Color.Unspecified,
+ val onSpinnerHeaderContainer: Color = Color.Unspecified,
+ val spinnerItemContainer: Color = Color.Unspecified,
+ val onSpinnerItemContainer: Color = Color.Unspecified,
)
internal val LocalColorScheme = staticCompositionLocalOf { SettingsColorScheme() }
@@ -65,6 +69,10 @@
secondaryText = tonalPalette.neutralVariant30,
primaryContainer = tonalPalette.primary90,
onPrimaryContainer = tonalPalette.neutral10,
+ spinnerHeaderContainer = tonalPalette.primary90,
+ onSpinnerHeaderContainer = tonalPalette.neutral10,
+ spinnerItemContainer = tonalPalette.secondary90,
+ onSpinnerItemContainer = tonalPalette.neutralVariant30,
)
}
@@ -87,5 +95,9 @@
secondaryText = tonalPalette.neutralVariant80,
primaryContainer = tonalPalette.secondary90,
onPrimaryContainer = tonalPalette.neutral10,
+ spinnerHeaderContainer = tonalPalette.primary90,
+ onSpinnerHeaderContainer = tonalPalette.neutral10,
+ spinnerItemContainer = tonalPalette.secondary90,
+ onSpinnerItemContainer = tonalPalette.neutralVariant30,
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt
similarity index 98%
rename from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt
rename to packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt
index 0454ac3..4f77a89 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.widget.ui
+package com.android.settingslib.spa.widget
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccessAlarm
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index c960254..b8e4360 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -26,13 +26,13 @@
import com.android.settingslib.spa.framework.compose.LocalNavController
@Composable
-internal fun NavigateUp() {
+internal fun NavigateBack() {
val navController = LocalNavController.current
val contentDescription = stringResource(
id = androidx.appcompat.R.string.abc_action_bar_up_description,
)
BackAction(contentDescription) {
- navController.navigateUp()
+ navController.navigateBack()
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index ee453f2..8b530b0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -49,7 +49,7 @@
modifier = Modifier.padding(SettingsDimension.itemPaddingAround),
)
},
- navigationIcon = { NavigateUp() },
+ navigationIcon = { NavigateBack() },
actions = actions,
colors = settingsTopAppBarColors(),
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
new file mode 100644
index 0000000..429b81a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.widget.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ArrowDropDown
+import androidx.compose.material.icons.outlined.ArrowDropUp
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun Spinner(options: List<String>, selectedIndex: Int, setIndex: (index: Int) -> Unit) {
+ if (options.isEmpty()) {
+ return
+ }
+
+ var expanded by rememberSaveable { mutableStateOf(false) }
+
+ Box(
+ modifier = Modifier
+ .padding(SettingsDimension.itemPadding)
+ .selectableGroup(),
+ ) {
+ val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
+ Button(
+ onClick = { expanded = true },
+ modifier = Modifier.height(36.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = SettingsTheme.colorScheme.spinnerHeaderContainer,
+ contentColor = SettingsTheme.colorScheme.onSpinnerHeaderContainer,
+ ),
+ contentPadding = contentPadding,
+ ) {
+ SpinnerText(options[selectedIndex])
+ Icon(
+ imageVector = when {
+ expanded -> Icons.Outlined.ArrowDropUp
+ else -> Icons.Outlined.ArrowDropDown
+ },
+ contentDescription = null,
+ )
+ }
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ modifier = Modifier.background(SettingsTheme.colorScheme.spinnerItemContainer),
+ offset = DpOffset(x = 0.dp, y = 4.dp),
+ ) {
+ options.forEachIndexed { index, option ->
+ DropdownMenuItem(
+ text = {
+ SpinnerText(
+ text = option,
+ modifier = Modifier.padding(end = 24.dp),
+ color = SettingsTheme.colorScheme.onSpinnerItemContainer,
+ )
+ },
+ onClick = {
+ expanded = false
+ setIndex(index)
+ },
+ contentPadding = contentPadding,
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun SpinnerText(
+ text: String,
+ modifier: Modifier = Modifier,
+ color: Color = Color.Unspecified,
+) {
+ Text(
+ text = text,
+ modifier = modifier.padding(end = SettingsDimension.itemPaddingEnd),
+ color = color,
+ style = MaterialTheme.typography.labelLarge,
+ )
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SpinnerPreview() {
+ SettingsTheme {
+ var selectedIndex by rememberSaveable { mutableStateOf(0) }
+ Spinner(
+ options = (1..3).map { "Option $it" },
+ selectedIndex = selectedIndex,
+ setIndex = { selectedIndex = it },
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp
index 037d8c4..1ce49fa 100644
--- a/packages/SettingsLib/Spa/tests/Android.bp
+++ b/packages/SettingsLib/Spa/tests/Android.bp
@@ -31,6 +31,7 @@
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui-test-junit4",
"androidx.compose.ui_ui-test-manifest",
+ "truth-prebuilt",
],
kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle
index be5a5ec..5f93a9f 100644
--- a/packages/SettingsLib/Spa/tests/build.gradle
+++ b/packages/SettingsLib/Spa/tests/build.gradle
@@ -63,5 +63,6 @@
androidTestImplementation(project(":spa"))
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$jetpack_compose_version")
+ androidTestImplementation 'com.google.truth:truth:1.1.3'
androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$jetpack_compose_version"
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt
new file mode 100644
index 0000000..1d95e33
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.widget
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsSliderTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ SettingsSlider(object : SettingsSliderModel {
+ override val title = "Slider"
+ override val initValue = 40
+ })
+ }
+
+ composeTestRule.onNodeWithText("Slider").assertIsDisplayed()
+ }
+
+ // TODO: Add more unit tests for SettingsSlider widget.
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/SpinnerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/SpinnerTest.kt
new file mode 100644
index 0000000..6c56d63
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/SpinnerTest.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.widget.ui
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SpinnerTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun spinner_initialState() {
+ var selectedIndex by mutableStateOf(0)
+ composeTestRule.setContent {
+ Spinner(
+ options = (1..3).map { "Option $it" },
+ selectedIndex = selectedIndex,
+ setIndex = { selectedIndex = it },
+ )
+ }
+
+ composeTestRule.onNodeWithText("Option 1").assertIsDisplayed()
+ composeTestRule.onNodeWithText("Option 2").assertDoesNotExist()
+ assertThat(selectedIndex).isEqualTo(0)
+ }
+
+ @Test
+ fun spinner_canChangeState() {
+ var selectedIndex by mutableStateOf(0)
+ composeTestRule.setContent {
+ Spinner(
+ options = (1..3).map { "Option $it" },
+ selectedIndex = selectedIndex,
+ setIndex = { selectedIndex = it },
+ )
+ }
+
+ composeTestRule.onNodeWithText("Option 1").performClick()
+ composeTestRule.onNodeWithText("Option 2").performClick()
+
+ composeTestRule.onNodeWithText("Option 1").assertDoesNotExist()
+ composeTestRule.onNodeWithText("Option 2").assertIsDisplayed()
+ assertThat(selectedIndex).isEqualTo(1)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index dac79a0..eb9ce5e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -30,7 +30,6 @@
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
@@ -44,7 +43,7 @@
private const val USER_ID = "userId"
internal class TogglePermissionAppInfoPageProvider(
- private val factory: TogglePermissionAppListModelFactory,
+ private val appListTemplate: TogglePermissionAppListTemplate,
) : SettingsPageProvider {
override val name = NAME
@@ -57,10 +56,10 @@
@Composable
override fun Page(arguments: Bundle?) {
checkNotNull(arguments)
- val permission = checkNotNull(arguments.getString(PERMISSION))
+ val permissionType = checkNotNull(arguments.getString(PERMISSION))
val packageName = checkNotNull(arguments.getString(PACKAGE_NAME))
val userId = arguments.getInt(USER_ID)
- val listModel = rememberContext { context -> factory.createModel(permission, context) }
+ val listModel = appListTemplate.rememberModel(permissionType)
TogglePermissionAppInfoPage(listModel, packageName, userId)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
similarity index 73%
rename from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
rename to packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index e1354bd..3cc5854 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -47,20 +47,14 @@
fun setAllowed(record: T, newAllowed: Boolean)
}
-interface TogglePermissionAppListModelFactory {
- fun createModel(
- permission: String,
- context: Context,
- ): TogglePermissionAppListModel<out AppRecord>
+interface TogglePermissionAppListProvider {
+ val permissionType: String
- fun createPageProviders(): List<SettingsPageProvider> = listOf(
- TogglePermissionAppListPageProvider(this),
- TogglePermissionAppInfoPageProvider(this),
- )
+ fun createModel(context: Context): TogglePermissionAppListModel<out AppRecord>
@Composable
- fun EntryItem(permissionType: String) {
- val listModel = rememberModel(permissionType)
+ fun EntryItem() {
+ val listModel = rememberContext(::createModel)
Preference(
object : PreferenceModel {
override val title = stringResource(listModel.pageTitleResId)
@@ -68,8 +62,28 @@
}
)
}
+
+ /**
+ * Gets the route to the toggle permission App List page.
+ *
+ * Expose route to enable enter from non-SPA pages.
+ */
+ fun getRoute(): String =
+ TogglePermissionAppListPageProvider.getRoute(permissionType)
}
-@Composable
-internal fun TogglePermissionAppListModelFactory.rememberModel(permission: String) =
- rememberContext { context -> createModel(permission, context) }
+class TogglePermissionAppListTemplate(
+ allProviders: List<TogglePermissionAppListProvider>,
+) {
+ private val listModelProviderMap = allProviders.associateBy { it.permissionType }
+
+ fun createPageProviders(): List<SettingsPageProvider> = listOf(
+ TogglePermissionAppListPageProvider(this),
+ TogglePermissionAppInfoPageProvider(this),
+ )
+
+ @Composable
+ internal fun rememberModel(permissionType: String) = rememberContext { context ->
+ listModelProviderMap.getValue(permissionType).createModel(context)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 6d00d56..f2eb962 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -38,7 +38,7 @@
private const val PERMISSION = "permission"
internal class TogglePermissionAppListPageProvider(
- private val factory: TogglePermissionAppListModelFactory,
+ private val appListTemplate: TogglePermissionAppListTemplate,
) : SettingsPageProvider {
override val name = NAME
@@ -55,7 +55,7 @@
@Composable
private fun TogglePermissionAppList(permissionType: String) {
- val listModel = factory.rememberModel(permissionType)
+ val listModel = appListTemplate.rememberModel(permissionType)
val context = LocalContext.current
val internalListModel = remember {
TogglePermissionInternalAppListModel(context, listModel)
@@ -75,8 +75,14 @@
}
companion object {
+ /**
+ * Gets the route to this page.
+ *
+ * Expose route to enable enter from non-SPA pages.
+ */
+ internal fun getRoute(permissionType: String) = "$NAME/$permissionType"
@Composable
- internal fun navigator(permissionType: String) = navigator(route = "$NAME/$permissionType")
+ internal fun navigator(permissionType: String) = navigator(route = getRoute(permissionType))
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 1940986..b64dcca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -43,8 +43,6 @@
public class A2dpProfile implements LocalBluetoothProfile {
private static final String TAG = "A2dpProfile";
- private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
-
private Context mContext;
private BluetoothA2dp mService;
@@ -333,7 +331,7 @@
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
index = 6;
break;
- case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS:
index = 7;
break;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 62c83cf..51812f0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -367,8 +367,14 @@
if (bondState == BluetoothDevice.BOND_NONE) {
// Check if we need to remove other Coordinated set member devices / Hearing Aid
// devices
+ if (DEBUG) {
+ Log.d(TAG, "BondStateChangedHandler: cachedDevice.getGroupId() = "
+ + cachedDevice.getGroupId() + ", cachedDevice.getHiSyncId()= "
+ + cachedDevice.getHiSyncId());
+ }
if (cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
|| cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
+ Log.d(TAG, "BondStateChangedHandler: Start onDeviceUnpaired");
mDeviceManager.onDeviceUnpaired(cachedDevice);
}
int reason = intent.getIntExtra(BluetoothDevice.EXTRA_UNBOND_REASON,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 32d5b78..3ca94db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1431,11 +1431,10 @@
* first connected device in the coordinated set, and then switch the content of the main
* device and member devices.
*
- * @param prevMainDevice the previous Main device, it will be added into the member device set.
- * @param newMainDevice the new Main device, it will be removed from the member device set.
+ * @param newMainDevice the new Main device which is from the previous main device's member
+ * list.
*/
- public void switchMemberDeviceContent(CachedBluetoothDevice prevMainDevice,
- CachedBluetoothDevice newMainDevice) {
+ public void switchMemberDeviceContent(CachedBluetoothDevice newMainDevice) {
// Backup from main device
final BluetoothDevice tmpDevice = mDevice;
final short tmpRssi = mRssi;
@@ -1444,8 +1443,7 @@
mDevice = newMainDevice.mDevice;
mRssi = newMainDevice.mRssi;
mJustDiscovered = newMainDevice.mJustDiscovered;
- addMemberDevice(prevMainDevice);
- mMemberDevices.remove(newMainDevice);
+
// Set sub device from backup
newMainDevice.mDevice = tmpDevice;
newMainDevice.mRssi = tmpRssi;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index cd3242a..26a2080 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -17,6 +17,7 @@
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -317,12 +318,14 @@
}
public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
+ device.setGroupId(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(device);
final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
if (!memberDevices.isEmpty()) {
// Main device is unpaired, to unpair the member device
for (CachedBluetoothDevice memberDevice : memberDevices) {
memberDevice.unpair();
+ memberDevice.setGroupId(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
device.removeMemberDevice(memberDevice);
}
} else if (mainDevice != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index fc70ba4..9b38238 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -231,7 +231,7 @@
// When both LE Audio devices are disconnected, receiving member device
// connection. To switch content and dispatch to notify UI change
mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
- mainDevice.switchMemberDeviceContent(mainDevice, cachedDevice);
+ mainDevice.switchMemberDeviceContent(cachedDevice);
mainDevice.refresh();
// It is necessary to do remove and add for updating the mapping on
// preference and device
@@ -255,10 +255,11 @@
for (CachedBluetoothDevice device: memberSet) {
if (device.isConnected()) {
+ log("set device: " + device + " as the main device");
// Main device is disconnected and sub device is connected
// To copy data from sub device to main device
mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
- cachedDevice.switchMemberDeviceContent(device, cachedDevice);
+ cachedDevice.switchMemberDeviceContent(device);
cachedDevice.refresh();
// It is necessary to do remove and add for updating the mapping on
// preference and device
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index bef1d9c..62552f91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
@@ -518,7 +519,8 @@
*/
@Test
public void onDeviceUnpaired_unpairCsipMainDevice() {
- when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setGroupId(1);
@@ -527,7 +529,12 @@
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
+
verify(mDevice2).removeBond();
+ assertThat(cachedDevice1.getGroupId()).isEqualTo(
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+ assertThat(cachedDevice2.getGroupId()).isEqualTo(
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
}
/**
@@ -536,6 +543,7 @@
@Test
public void onDeviceUnpaired_unpairCsipSubDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setGroupId(1);
@@ -544,7 +552,10 @@
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
+
verify(mDevice1).removeBond();
+ assertThat(cachedDevice2.getGroupId()).isEqualTo(
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
}
/**
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 76c066c..79e9938 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
@@ -1050,4 +1050,23 @@
assertThat(mCachedDevice.mDrawableCache.size()).isEqualTo(0);
}
+
+ @Test
+ public void switchMemberDeviceContent_switchMainDevice_switchesSuccessful() {
+ mCachedDevice.mRssi = RSSI_1;
+ mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
+ mSubCachedDevice.mRssi = RSSI_2;
+ mSubCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+
+ mCachedDevice.switchMemberDeviceContent(mSubCachedDevice);
+
+ assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
+ assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
+ assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+ assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
+ assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
+ assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ }
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 78dea89..7649de6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -85,7 +85,6 @@
<uses-permission android:name="android.permission.CONTROL_VPN" />
<uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
<uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL"/>
- <uses-permission android:name="android.permission.NETWORK_STACK"/>
<!-- Physical hardware -->
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
@@ -95,6 +94,7 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
<uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
+ <uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
<!-- ActivityManager -->
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
@@ -152,6 +152,9 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
<uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
+ <!-- For auto-grant the access to the Settings' slice preferences, e.g. volume slices. -->
+ <uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
+
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
@@ -956,5 +959,13 @@
</intent-filter>
</receiver>
+ <receiver android:name=".volume.VolumePanelDialogReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.settings.panel.action.VOLUME" />
+ <action android:name="com.android.systemui.action.LAUNCH_VOLUME_PANEL_DIALOG" />
+ <action android:name="com.android.systemui.action.DISMISS_VOLUME_PANEL_DIALOG" />
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
new file mode 100644
index 0000000..3ca8dfe
--- /dev/null
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -0,0 +1,870 @@
++packages/SystemUI
+-packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+-packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+-packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+-packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+-packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+-packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt
+-packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
+-packages/SystemUI/checks/src/com/android/internal/systemui/lint/GetMainLooperViaContextDetector.kt
+-packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt
+-packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt
+-packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+-packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt
+-packages/SystemUI/checks/tests/com/android/systemui/lint/BroadcastSentViaContextDetectorTest.kt
+-packages/SystemUI/checks/tests/com/android/systemui/lint/GetMainLooperViaContextDetectorTest.kt
+-packages/SystemUI/checks/tests/com/android/systemui/lint/RegisterReceiverViaContextDetectorTest.kt
+-packages/SystemUI/checks/tests/com/android/systemui/lint/SoftwareBitmapDetectorTest.kt
+-packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+-packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+-packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
+-packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+-packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
+-packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+-packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
+-packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerManager.kt
+-packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
+-packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
+-packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
+-packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
+-packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+-packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+-packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+-packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
+-packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
+-packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+-packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
+-packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+-packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt
+-packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt
+-packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+-packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/BootCompleteCache.kt
+-packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt
+-packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+-packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
+-packages/SystemUI/src/com/android/systemui/DarkReceiverImpl.kt
+-packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+-packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
+-packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+-packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
+-packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
+-packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt
+-packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
+-packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt
+-packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt
+-packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt
+-packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt
+-packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+-packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt
+-packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+-packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcherStartable.kt
+-packages/SystemUI/src/com/android/systemui/broadcast/BroadcastSender.kt
+-packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt
+-packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+-packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
+-packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+-packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
+-packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
+-packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+-packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
+-packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
+-packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
+-packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+-packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt
+-packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
+-packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
+-packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+-packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsFeatureEnabled.kt
+-packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt
+-packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+-packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
+-packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+-packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
+-packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
+-packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
+-packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
+-packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
+-packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
+-packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
+-packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
+-packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt
+-packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt
+-packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+-packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
+-packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
+-packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
+-packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+-packages/SystemUI/src/com/android/systemui/dump/DumpsysTableLogger.kt
+-packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt
+-packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt
+-packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+-packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
+-packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
+-packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+-packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+-packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+-packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
+-packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
+-packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt
+-packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt
+-packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
+-packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
+-packages/SystemUI/src/com/android/systemui/media/AnimationBindHandler.kt
+-packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
+-packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt
+-packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
+-packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+-packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt
+-packages/SystemUI/src/com/android/systemui/media/LocalMediaManagerFactory.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaCarouselControllerLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaFeatureFlag.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaTimeoutLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
+-packages/SystemUI/src/com/android/systemui/media/MediaViewLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt
+-packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
+-packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+-packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+-packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+-packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
+-packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
+-packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
+-packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
+-packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+-packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+-packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionCli.kt
+-packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
+-packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
+-packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
+-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
+-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+-packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt
+-packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt
+-packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt
+-packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+-packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+-packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+-packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+-packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+-packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+-packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+-packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+-packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
+-packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
+-packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+-packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
+-packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
+-packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+-packages/SystemUI/src/com/android/systemui/qs/VisibilityChangedDispatcher.kt
+-packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+-packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
+-packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
+-packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt
+-packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+-packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt
+-packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
+-packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+-packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
+-packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
+-packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+-packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+-packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+-packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+-packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+-packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+-packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
+-packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+-packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
+-packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
+-packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+-packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt
+-packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
+-packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+-packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
+-packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+-packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
+-packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
+-packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt
+-packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+-packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+-packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+-packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
+-packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt
+-packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt
+-packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
+-packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+-packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt
+-packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt
+-packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+-packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+-packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+-packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+-packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
+-packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
+-packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt
+-packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt
+-packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt
+-packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+-packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+-packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
+-packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandRegistry.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiState.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/FeedbackIcon.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTracker.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtender.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderExtensions.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/RemoteInputViewModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarIconBlocklist.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiActivityModel.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/WalletController.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/WalletControllerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+-packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
+-packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
+-packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
+-packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt
+-packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+-packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
+-packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+-packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
+-packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
+-packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+-packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
+-packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt
+-packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt
+-packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt
+-packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+-packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
+-packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
+-packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt
+-packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
+-packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
+-packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt
+-packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
+-packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt
+-packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt
+-packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt
+-packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+-packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt
+-packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
+-packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
+-packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
+-packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+-packages/SystemUI/src/com/android/systemui/util/animation/AnimationUtil.kt
+-packages/SystemUI/src/com/android/systemui/util/animation/MeasurementInput.kt
+-packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+-packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
+-packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
+-packages/SystemUI/src/com/android/systemui/util/collection/RingBuffer.kt
+-packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt
+-packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
+-packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
+-packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+-packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+-packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt
+-packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt
+-packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
+-packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+-packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt
+-packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
+-packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt
+-packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/dump/DumpsysTableLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferFreezerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
+-packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt
+-packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/AnimationBindHandlerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplingInstanceTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
+-packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/ListenerSetTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/collection/RingBufferTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
+-packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+-packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+-packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
+-packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
+-packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
diff --git a/packages/SystemUI/res/layout/dream_overlay_media_entry_chip.xml b/packages/SystemUI/res/layout/dream_overlay_media_entry_chip.xml
new file mode 100644
index 0000000..50f3ffc
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_media_entry_chip.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+-->
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/media_entry_chip"
+ android:layout_height="@dimen/keyguard_affordance_fixed_height"
+ android:layout_width="@dimen/keyguard_affordance_fixed_width"
+ android:layout_gravity="bottom|start"
+ android:scaleType="center"
+ android:tint="?android:attr/textColorPrimary"
+ android:src="@drawable/ic_music_note"
+ android:background="@drawable/keyguard_bottom_affordance_bg"
+ android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
+ android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
+ android:contentDescription="@string/controls_media_title" />
diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml
index d886806..ae8e38e 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip.xml
@@ -16,7 +16,7 @@
<!-- Wrap in a frame layout so that we can update the margins on the inner layout. (Since this view
is the root view of a window, we cannot change the root view's margins.) -->
<!-- Alphas start as 0 because the view will be animated in. -->
-<FrameLayout
+<com.android.systemui.media.taptotransfer.sender.MediaTttChipRootView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/media_ttt_sender_chip"
@@ -97,4 +97,4 @@
/>
</LinearLayout>
-</FrameLayout>
+</com.android.systemui.media.taptotransfer.sender.MediaTttChipRootView>
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
deleted file mode 100644
index ec00429..0000000
--- a/packages/SystemUI/res/layout/people_strip.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 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.
- -->
-
-<com.android.systemui.statusbar.notification.stack.PeopleHubView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_section_header_height"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:focusable="true"
- android:clickable="true"
->
-
- <LinearLayout
- android:id="@+id/people_list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginEnd="8dp"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:forceHasOverlappingRendering="false"
- android:clipChildren="false">
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:layout_weight="1"
- android:forceHasOverlappingRendering="false">
-
- <TextView
- android:id="@+id/header_label"
- style="@style/TextAppearance.NotificationSectionHeaderButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/notification_section_header_conversations"
- />
-
- </FrameLayout>
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- </LinearLayout>
-
-</com.android.systemui.statusbar.notification.stack.PeopleHubView>
diff --git a/packages/SystemUI/res/layout/volume_panel_dialog.xml b/packages/SystemUI/res/layout/volume_panel_dialog.xml
new file mode 100644
index 0000000..99a1b5c
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel_dialog.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/volume_panel_dialog"
+ android:layout_width="@dimen/large_dialog_width"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Widget.SliceView.Panel"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_marginTop="@dimen/dialog_top_padding"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/volume_panel_dialog_title"
+ android:ellipsize="end"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sound_settings"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"/>
+ </LinearLayout>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/volume_panel_parent_layout"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:minHeight="304dp"
+ android:layout_weight="1"
+ android:overScrollMode="never"/>
+
+ <LinearLayout
+ android:id="@+id/button_layout"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/dialog_button_vertical_padding"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
+ android:baselineAligned="false"
+ android:clickable="false"
+ android:focusable="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="start|center_vertical"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/settings_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/volume_panel_dialog_settings_button"
+ android:ellipsize="end"
+ android:maxLines="1"
+ style="@style/Widget.Dialog.Button.BorderButton"
+ android:clickable="true"
+ android:focusable="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/dialog_button_horizontal_padding"
+ android:layout_gravity="end|center_vertical">
+ <Button
+ android:id="@+id/done_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inline_done_button"
+ style="@style/Widget.Dialog.Button"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:clickable="true"
+ android:focusable="true"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_panel_slice_slider_row.xml b/packages/SystemUI/res/layout/volume_panel_slice_slider_row.xml
new file mode 100644
index 0000000..d1303ed
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel_slice_slider_row.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/slice_slider_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <androidx.slice.widget.SliceView
+ android:id="@+id/slice_view"
+ style="@style/Widget.SliceView.Panel.Slider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingVertical="@dimen/volume_panel_slice_vertical_padding"
+ android:paddingHorizontal="@dimen/volume_panel_slice_horizontal_padding"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e34d422..a19145d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -479,6 +479,10 @@
<dimen name="volume_tool_tip_arrow_corner_radius">2dp</dimen>
+ <!-- Volume panel slices dimensions -->
+ <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
+ <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
+
<!-- Size of each item in the ringer selector drawer. -->
<dimen name="volume_ringer_drawer_item_size">42dp</dimen>
<dimen name="volume_ringer_drawer_item_size_half">21dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index dca5ea8..f88f46f 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -131,7 +131,11 @@
<!-- For StatusIconContainer to tag its icon views -->
<item type="id" name="status_bar_view_state_tag" />
+ <!-- Default display cutout on the physical top of screen -->
<item type="id" name="display_cutout" />
+ <item type="id" name="display_cutout_left" />
+ <item type="id" name="display_cutout_right" />
+ <item type="id" name="display_cutout_bottom" />
<item type="id" name="row_tag_for_content_view" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bfdb170..c7b2ff3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1139,6 +1139,11 @@
<!-- Content description for accessibility: Hint if click will disable. [CHAR LIMIT=NONE] -->
<string name="volume_odi_captions_hint_disable">disable</string>
+ <!-- Sound and vibration settings dialog title. [CHAR LIMIT=30] -->
+ <string name="sound_settings">Sound & vibration</string>
+ <!-- Label for button to go to sound settings screen [CHAR_LIMIT=30] -->
+ <string name="volume_panel_dialog_settings_button">Settings</string>
+
<!-- content description for audio output chooser [CHAR LIMIT=NONE]-->
<!-- Screen pinning dialog title. -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f7acf06..6d1bcbb 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -935,6 +935,10 @@
<item name="rowStyle">@style/SliceRow</item>
</style>
+ <style name="Widget.SliceView.Panel.Slider">
+ <item name="rowStyle">@style/SliceRow.Slider</item>
+ </style>
+
<style name="SliceRow">
<!-- 2dp start padding for the start icon -->
<item name="titleItemStartPadding">2dp</item>
@@ -956,6 +960,26 @@
<item name="actionDividerHeight">32dp</item>
</style>
+ <style name="SliceRow.Slider">
+ <!-- Padding between content and the start icon is 5dp -->
+ <item name="contentStartPadding">5dp</item>
+ <item name="contentEndPadding">0dp</item>
+
+ <!-- 0dp start padding for the end item -->
+ <item name="endItemStartPadding">0dp</item>
+ <!-- 8dp end padding for the end item -->
+ <item name="endItemEndPadding">8dp</item>
+
+ <item name="titleSize">20sp</item>
+ <!-- Align text with slider -->
+ <item name="titleStartPadding">11dp</item>
+ <item name="subContentStartPadding">11dp</item>
+
+ <!-- Padding for indeterminate progress bar -->
+ <item name="progressBarStartPadding">12dp</item>
+ <item name="progressBarEndPadding">16dp</item>
+ </style>
+
<style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">24sp</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index e1957c0..93175e1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -418,6 +418,7 @@
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, state);
getCurrentSecurityController().onResume(reason);
+ updateSideFpsVisibility();
}
mView.onResume(
mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 6c452bd..d718a24 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -45,7 +45,7 @@
fun e(@CompileTimeConstant msg: String) = log(msg, ERROR)
- fun v(@CompileTimeConstant msg: String) = log(msg, VERBOSE)
+ fun v(@CompileTimeConstant msg: String) = log(msg, ERROR)
fun w(@CompileTimeConstant msg: String) = log(msg, WARNING)
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardViewMediatorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardViewMediatorLogger.kt
deleted file mode 100644
index f54bf02..0000000
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardViewMediatorLogger.kt
+++ /dev/null
@@ -1,690 +0,0 @@
-/*
- * 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.keyguard.logging
-
-import android.os.RemoteException
-import android.view.WindowManagerPolicyConstants
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
-import com.android.systemui.log.LogLevel.WTF
-import com.android.systemui.log.dagger.KeyguardViewMediatorLog
-import javax.inject.Inject
-
-private const val TAG = "KeyguardViewMediatorLog"
-
-@SysUISingleton
-class KeyguardViewMediatorLogger @Inject constructor(
- @KeyguardViewMediatorLog private val logBuffer: LogBuffer,
-) {
-
- fun logFailedLoadLockSound(soundPath: String) {
- logBuffer.log(
- TAG,
- WARNING,
- { str1 = soundPath },
- { "failed to load lock sound from $str1" }
- )
- }
-
- fun logFailedLoadUnlockSound(soundPath: String) {
- logBuffer.log(
- TAG,
- WARNING,
- { str1 = soundPath },
- { "failed to load unlock sound from $str1" }
- )
- }
-
- fun logFailedLoadTrustedSound(soundPath: String) {
- logBuffer.log(
- TAG,
- WARNING,
- { str1 = soundPath },
- { "failed to load trusted sound from $str1" }
- )
- }
-
- fun logOnSystemReady() {
- logBuffer.log(TAG, DEBUG, "onSystemReady")
- }
-
- fun logOnStartedGoingToSleep(offReason: Int) {
- val offReasonString = WindowManagerPolicyConstants.offReasonToString(offReason)
- logBuffer.log(
- TAG,
- DEBUG,
- { str1 = offReasonString },
- { "onStartedGoingToSleep($str1)" }
- )
- }
-
- fun logPendingExitSecureCallbackCancelled() {
- logBuffer.log(TAG, DEBUG, "pending exit secure callback cancelled")
- }
-
- fun logFailedOnKeyguardExitResultFalse(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onKeyguardExitResult(false)",
- remoteException
- )
- }
-
- fun logOnFinishedGoingToSleep(offReason: Int) {
- val offReasonString = WindowManagerPolicyConstants.offReasonToString(offReason)
- logBuffer.log(
- TAG,
- DEBUG,
- { str1 = offReasonString },
- { "onFinishedGoingToSleep($str1)" }
- )
- }
-
- fun logPinLockRequestedStartingKeyguard() {
- logBuffer.log(TAG, INFO, "PIN lock requested, starting keyguard")
- }
-
- fun logUserSwitching(userId: Int) {
- logBuffer.log(
- TAG,
- DEBUG,
- { int1 = userId },
- { "onUserSwitching $int1" }
- )
- }
-
- fun logOnUserSwitchComplete(userId: Int) {
- logBuffer.log(
- TAG,
- DEBUG,
- { int1 = userId },
- { "onUserSwitchComplete $int1" }
- )
- }
-
- fun logOnSimStateChanged(subId: Int, slotId: Int, simState: String) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- int1 = subId
- int2 = slotId
- str1 = simState
- },
- { "onSimStateChanged(subId=$int1, slotId=$int2, state=$str1)" }
- )
- }
-
- fun logFailedToCallOnSimSecureStateChanged(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onSimSecureStateChanged",
- remoteException
- )
- }
-
- fun logIccAbsentIsNotShowing() {
- logBuffer.log(TAG, DEBUG, "ICC_ABSENT isn't showing, we need to show the " +
- "keyguard since the device isn't provisioned yet.")
- }
-
- fun logSimMovedToAbsent() {
- logBuffer.log(TAG, DEBUG, "SIM moved to ABSENT when the " +
- "previous state was locked. Reset the state.")
- }
-
- fun logIntentValueIccLocked() {
- logBuffer.log(TAG, DEBUG, "INTENT_VALUE_ICC_LOCKED and keyguard isn't " +
- "showing; need to show keyguard so user can enter sim pin")
- }
-
- fun logPermDisabledKeyguardNotShowing() {
- logBuffer.log(TAG, DEBUG, "PERM_DISABLED and keyguard isn't showing.")
- }
-
- fun logPermDisabledResetStateLocked() {
- logBuffer.log(TAG, DEBUG, "PERM_DISABLED, resetStateLocked to show permanently " +
- "disabled message in lockscreen.")
- }
-
- fun logReadyResetState(showing: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = showing },
- { "READY, reset state? $bool1"}
- )
- }
-
- fun logSimMovedToReady() {
- logBuffer.log(TAG, DEBUG, "SIM moved to READY when the previously was locked. " +
- "Reset the state.")
- }
-
- fun logUnspecifiedSimState(simState: Int) {
- logBuffer.log(
- TAG,
- DEBUG,
- { int1 = simState },
- { "Unspecific state: $int1" }
- )
- }
-
- fun logOccludeLaunchAnimationCancelled(occluded: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = occluded },
- { "Occlude launch animation cancelled. Occluded state is now: $bool1"}
- )
- }
-
- fun logActivityLaunchAnimatorLaunchContainerChanged() {
- logBuffer.log(TAG, WTF, "Someone tried to change the launch container for the " +
- "ActivityLaunchAnimator, which should never happen.")
- }
-
- fun logVerifyUnlock() {
- logBuffer.log(TAG, DEBUG, "verifyUnlock")
- }
-
- fun logIgnoreUnlockDeviceNotProvisioned() {
- logBuffer.log(TAG, DEBUG, "ignoring because device isn't provisioned")
- }
-
- fun logFailedToCallOnKeyguardExitResultFalse(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onKeyguardExitResult(false)",
- remoteException
- )
- }
-
- fun logVerifyUnlockCalledNotExternallyDisabled() {
- logBuffer.log(TAG, WARNING, "verifyUnlock called when not externally disabled")
- }
-
- fun logSetOccluded(isOccluded: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = isOccluded },
- { "setOccluded($bool1)" }
- )
- }
-
- fun logHandleSetOccluded(isOccluded: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = isOccluded },
- { "handleSetOccluded($bool1)" }
- )
- }
-
- fun logIgnoreHandleShow() {
- logBuffer.log(TAG, DEBUG, "ignoring handleShow because system is not ready.")
- }
-
- fun logHandleShow() {
- logBuffer.log(TAG, DEBUG, "handleShow")
- }
-
- fun logHandleHide() {
- logBuffer.log(TAG, DEBUG, "handleHide")
- }
-
- fun logSplitSystemUserQuitUnlocking() {
- logBuffer.log(TAG, DEBUG, "Split system user, quit unlocking.")
- }
-
- fun logHandleStartKeyguardExitAnimation(startTime: Long, fadeoutDuration: Long) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- long1 = startTime
- long2 = fadeoutDuration
- },
- { "handleStartKeyguardExitAnimation startTime=$long1 fadeoutDuration=$long2" }
- )
- }
-
- fun logHandleVerifyUnlock() {
- logBuffer.log(TAG, DEBUG, "handleVerifyUnlock")
- }
-
- fun logHandleNotifyStartedGoingToSleep() {
- logBuffer.log(TAG, DEBUG, "handleNotifyStartedGoingToSleep")
- }
-
- fun logHandleNotifyFinishedGoingToSleep() {
- logBuffer.log(TAG, DEBUG, "handleNotifyFinishedGoingToSleep")
- }
-
- fun logHandleNotifyWakingUp() {
- logBuffer.log(TAG, DEBUG, "handleNotifyWakingUp")
- }
-
- fun logHandleReset() {
- logBuffer.log(TAG, DEBUG, "handleReset")
- }
-
- fun logKeyguardDone() {
- logBuffer.log(TAG, DEBUG, "keyguardDone")
- }
-
- fun logKeyguardDonePending() {
- logBuffer.log(TAG, DEBUG, "keyguardDonePending")
- }
-
- fun logKeyguardGone() {
- logBuffer.log(TAG, DEBUG, "keyguardGone")
- }
-
- fun logUnoccludeAnimationCancelled(isOccluded: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = isOccluded },
- { "Unocclude animation cancelled. Occluded state is now: $bool1" }
- )
- }
-
- fun logShowLocked() {
- logBuffer.log(TAG, DEBUG, "showLocked")
- }
-
- fun logHideLocked() {
- logBuffer.log(TAG, DEBUG, "hideLocked")
- }
-
- fun logResetStateLocked() {
- logBuffer.log(TAG, DEBUG, "resetStateLocked")
- }
-
- fun logNotifyStartedGoingToSleep() {
- logBuffer.log(TAG, DEBUG, "notifyStartedGoingToSleep")
- }
-
- fun logNotifyFinishedGoingToSleep() {
- logBuffer.log(TAG, DEBUG, "notifyFinishedGoingToSleep")
- }
-
- fun logNotifyStartedWakingUp() {
- logBuffer.log(TAG, DEBUG, "notifyStartedWakingUp")
- }
-
- fun logDoKeyguardShowingLockScreen() {
- logBuffer.log(TAG, DEBUG, "doKeyguard: showing the lock screen")
- }
-
- fun logDoKeyguardNotShowingLockScreenOff() {
- logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because lockscreen is off")
- }
-
- fun logDoKeyguardNotShowingDeviceNotProvisioned() {
- logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because device isn't " +
- "provisioned and the sim is not locked or missing")
- }
-
- fun logDoKeyguardNotShowingAlreadyShowing() {
- logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because it is already showing")
- }
-
- fun logDoKeyguardNotShowingBootingCryptkeeper() {
- logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because booting to cryptkeeper")
- }
-
- fun logDoKeyguardNotShowingExternallyDisabled() {
- logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because externally disabled")
- }
-
- fun logFailedToCallOnDeviceProvisioned(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onDeviceProvisioned",
- remoteException
- )
- }
-
- fun logMaybeHandlePendingLockNotHandling() {
- logBuffer.log(TAG, DEBUG, "#maybeHandlePendingLock: not handling because the " +
- "screen off animation's isKeyguardShowDelayed() returned true. This should be " +
- "handled soon by #onStartedWakingUp, or by the end actions of the " +
- "screen off animation.")
- }
-
- fun logMaybeHandlePendingLockKeyguardGoingAway() {
- logBuffer.log(TAG, DEBUG, "#maybeHandlePendingLock: not handling because the " +
- "keyguard is going away. This should be handled shortly by " +
- "StatusBar#finishKeyguardFadingAway.")
- }
-
- fun logMaybeHandlePendingLockHandling() {
- logBuffer.log(TAG, DEBUG, "#maybeHandlePendingLock: handling pending lock; " +
- "locking keyguard.")
- }
-
- fun logSetAlarmToTurnOffKeyguard(delayedShowingSequence: Int) {
- logBuffer.log(
- TAG,
- DEBUG,
- { int1 = delayedShowingSequence },
- { "setting alarm to turn off keyguard, seq = $int1" }
- )
- }
-
- fun logOnStartedWakingUp(delayedShowingSequence: Int) {
- logBuffer.log(
- TAG,
- DEBUG,
- { int1 = delayedShowingSequence },
- { "onStartedWakingUp, seq = $int1" }
- )
- }
-
- fun logSetKeyguardEnabled(enabled: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = enabled },
- { "setKeyguardEnabled($bool1)" }
- )
- }
-
- fun logIgnoreVerifyUnlockRequest() {
- logBuffer.log(TAG, DEBUG, "in process of verifyUnlock request, ignoring")
- }
-
- fun logRememberToReshowLater() {
- logBuffer.log(TAG, DEBUG, "remembering to reshow, hiding keyguard, disabling " +
- "status bar expansion")
- }
-
- fun logPreviouslyHiddenReshow() {
- logBuffer.log(TAG, DEBUG, "previously hidden, reshowing, reenabling status " +
- "bar expansion")
- }
-
- fun logOnKeyguardExitResultFalseResetting() {
- logBuffer.log(TAG, DEBUG, "onKeyguardExitResult(false), resetting")
- }
-
- fun logWaitingUntilKeyguardVisibleIsFalse() {
- logBuffer.log(TAG, DEBUG, "waiting until mWaitingUntilKeyguardVisible is false")
- }
-
- fun logDoneWaitingUntilKeyguardVisible() {
- logBuffer.log(TAG, DEBUG, "done waiting for mWaitingUntilKeyguardVisible")
- }
-
- fun logUnoccludeAnimatorOnAnimationStart() {
- logBuffer.log(TAG, DEBUG, "UnoccludeAnimator#onAnimationStart. " +
- "Set occluded = false.")
- }
-
- fun logNoAppsProvidedToUnoccludeRunner() {
- logBuffer.log(TAG, DEBUG, "No apps provided to unocclude runner; " +
- "skipping animation and unoccluding.")
- }
-
- fun logReceivedDelayedKeyguardAction(sequence: Int, delayedShowingSequence: Int) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- int1 = sequence
- int2 = delayedShowingSequence
- },
- {
- "received DELAYED_KEYGUARD_ACTION with seq = $int1 " +
- "mDelayedShowingSequence = $int2"
- }
- )
- }
-
- fun logTimeoutWhileActivityDrawn() {
- logBuffer.log(TAG, WARNING, "Timeout while waiting for activity drawn")
- }
-
- fun logTryKeyguardDonePending(
- keyguardDonePending: Boolean,
- hideAnimationRun: Boolean,
- hideAnimationRunning: Boolean
- ) {
- logBuffer.log(TAG, DEBUG,
- {
- bool1 = keyguardDonePending
- bool2 = hideAnimationRun
- bool3 = hideAnimationRunning
- },
- { "tryKeyguardDone: pending - $bool1, animRan - $bool2 animRunning - $bool3" }
- )
- }
-
- fun logTryKeyguardDonePreHideAnimation() {
- logBuffer.log(TAG, DEBUG, "tryKeyguardDone: starting pre-hide animation")
- }
-
- fun logHandleKeyguardDone() {
- logBuffer.log(TAG, DEBUG, "handleKeyguardDone")
- }
-
- fun logDeviceGoingToSleep() {
- logBuffer.log(TAG, INFO, "Device is going to sleep, aborting keyguardDone")
- }
-
- fun logFailedToCallOnKeyguardExitResultTrue(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onKeyguardExitResult(true)",
- remoteException
- )
- }
-
- fun logHandleKeyguardDoneDrawing() {
- logBuffer.log(TAG, DEBUG, "handleKeyguardDoneDrawing")
- }
-
- fun logHandleKeyguardDoneDrawingNotifyingKeyguardVisible() {
- logBuffer.log(TAG, DEBUG, "handleKeyguardDoneDrawing: notifying " +
- "mWaitingUntilKeyguardVisible")
- }
-
- fun logUpdateActivityLockScreenState(showing: Boolean, aodShowing: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- bool1 = showing
- bool2 = aodShowing
- },
- { "updateActivityLockScreenState($bool1, $bool2)" }
- )
- }
-
- fun logFailedToCallSetLockScreenShown(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call setLockScreenShown",
- remoteException
- )
- }
-
- fun logKeyguardGoingAway() {
- logBuffer.log(TAG, DEBUG, "keyguardGoingAway")
- }
-
- fun logFailedToCallKeyguardGoingAway(keyguardFlag: Int, remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- ERROR,
- { int1 = keyguardFlag },
- { "Failed to call keyguardGoingAway($int1)" },
- remoteException
- )
- }
-
- fun logHideAnimationFinishedRunnable() {
- logBuffer.log(TAG, WARNING, "mHideAnimationFinishedRunnable#run")
- }
-
- fun logFailedToCallOnAnimationFinished(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onAnimationFinished",
- remoteException
- )
- }
-
- fun logFailedToCallOnAnimationStart(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onAnimationStart",
- remoteException
- )
- }
-
- fun logOnKeyguardExitRemoteAnimationFinished() {
- logBuffer.log(TAG, DEBUG, "onKeyguardExitRemoteAnimationFinished")
- }
-
- fun logSkipOnKeyguardExitRemoteAnimationFinished(
- cancelled: Boolean,
- surfaceBehindRemoteAnimationRunning: Boolean,
- surfaceBehindRemoteAnimationRequested: Boolean
- ) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- bool1 = cancelled
- bool2 = surfaceBehindRemoteAnimationRunning
- bool3 = surfaceBehindRemoteAnimationRequested
- },
- {
- "skip onKeyguardExitRemoteAnimationFinished cancelled=$bool1 " +
- "surfaceAnimationRunning=$bool2 " +
- "surfaceAnimationRequested=$bool3"
- }
- )
- }
-
- fun logOnKeyguardExitRemoteAnimationFinishedHideKeyguardView() {
- logBuffer.log(TAG, DEBUG, "onKeyguardExitRemoteAnimationFinished" +
- "#hideKeyguardViewAfterRemoteAnimation")
- }
-
- fun logSkipHideKeyguardViewAfterRemoteAnimation(
- dismissingFromSwipe: Boolean,
- wasShowing: Boolean
- ) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- bool1 = dismissingFromSwipe
- bool2 = wasShowing
- },
- {
- "skip hideKeyguardViewAfterRemoteAnimation dismissFromSwipe=$bool1 " +
- "wasShowing=$bool2"
- }
- )
- }
-
- fun logCouldNotGetStatusBarManager() {
- logBuffer.log(TAG, WARNING, "Could not get status bar manager")
- }
-
- fun logAdjustStatusBarLocked(
- showing: Boolean,
- occluded: Boolean,
- secure: Boolean,
- forceHideHomeRecentsButtons: Boolean,
- flags: String
- ) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- bool1 = showing
- bool2 = occluded
- bool3 = secure
- bool4 = forceHideHomeRecentsButtons
- str3 = flags
- },
- {
- "adjustStatusBarLocked: mShowing=$bool1 mOccluded=$bool2 isSecure=$bool3 " +
- "force=$bool4 --> flags=0x$str3"
- }
- )
- }
-
- fun logFailedToCallOnShowingStateChanged(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call onShowingStateChanged",
- remoteException
- )
- }
-
- fun logFailedToCallNotifyTrustedChangedLocked(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call notifyTrustedChangedLocked",
- remoteException
- )
- }
-
- fun logFailedToCallIKeyguardStateCallback(remoteException: RemoteException) {
- logBuffer.log(
- TAG,
- WARNING,
- "Failed to call to IKeyguardStateCallback",
- remoteException
- )
- }
-
- fun logOccludeAnimatorOnAnimationStart() {
- logBuffer.log(TAG, DEBUG, "OccludeAnimator#onAnimationStart. Set occluded = true.")
- }
-
- fun logOccludeAnimationCancelledByWm(isKeyguardOccluded: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = isKeyguardOccluded },
- { "Occlude animation cancelled by WM. Setting occluded state to: $bool1" }
- )
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
new file mode 100644
index 0000000..109be40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
@@ -0,0 +1,67 @@
+package com.android.systemui
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FlagListenable
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class ChooserSelector @Inject constructor(
+ context: Context,
+ private val featureFlags: FeatureFlags,
+ @Application private val coroutineScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher
+) : CoreStartable(context) {
+
+ private val packageManager = context.packageManager
+ private val chooserComponent = ComponentName.unflattenFromString(
+ context.resources.getString(ChooserSelectorResourceHelper.CONFIG_CHOOSER_ACTIVITY))
+
+ override fun start() {
+ coroutineScope.launch {
+ val listener = FlagListenable.Listener { event ->
+ if (event.flagId == Flags.CHOOSER_UNBUNDLED.id) {
+ launch { updateUnbundledChooserEnabled() }
+ event.requestNoRestart()
+ }
+ }
+ featureFlags.addListener(Flags.CHOOSER_UNBUNDLED, listener)
+ updateUnbundledChooserEnabled()
+
+ awaitCancellationAndThen { featureFlags.removeListener(listener) }
+ }
+ }
+
+ private suspend fun updateUnbundledChooserEnabled() {
+ setUnbundledChooserEnabled(withContext(bgDispatcher) {
+ featureFlags.isEnabled(Flags.CHOOSER_UNBUNDLED)
+ })
+ }
+
+ private fun setUnbundledChooserEnabled(enabled: Boolean) {
+ val newState = if (enabled) {
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ } else {
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ }
+ packageManager.setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
+ }
+
+ suspend inline fun awaitCancellation(): Nothing = suspendCancellableCoroutine { }
+ suspend inline fun awaitCancellationAndThen(block: () -> Unit): Nothing = try {
+ awaitCancellation()
+ } finally {
+ block()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt
rename to packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java
index 88e227b..7a2de7b 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java
@@ -14,10 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.log.dagger
+package com.android.systemui;
-import javax.inject.Qualifier
+import androidx.annotation.StringRes;
-/** A [com.android.systemui.log.LogBuffer] for KeyguardViewMediator. */
-@Qualifier
-annotation class KeyguardViewMediatorLog
\ No newline at end of file
+import com.android.internal.R;
+
+/** Helper class for referencing resources */
+class ChooserSelectorResourceHelper {
+
+ private ChooserSelectorResourceHelper() {
+ }
+
+ @StringRes
+ static final int CONFIG_CHOOSER_ACTIVITY = R.string.config_chooserActivity;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 614a87f..0cb4da4 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -87,8 +87,6 @@
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -311,8 +309,6 @@
@Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
@Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
- @Inject Lazy<NotificationGroupManagerLegacy> mNotificationGroupManager;
- @Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
@Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
@Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
@Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
@@ -523,8 +519,6 @@
mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
mProviders.put(NotificationLockscreenUserManager.class,
mNotificationLockscreenUserManager::get);
- mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
- mProviders.put(NotificationGroupManagerLegacy.class, mNotificationGroupManager::get);
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
mProviders.put(NotificationRemoteInputManager.class,
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4c400a8..2e13903 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -72,6 +72,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.decor.CutoutDecorProviderFactory;
import com.android.systemui.decor.DecorProvider;
import com.android.systemui.decor.DecorProviderFactory;
import com.android.systemui.decor.DecorProviderKt;
@@ -118,6 +119,13 @@
private static final boolean VERBOSE = false;
static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
+ private static final int[] DISPLAY_CUTOUT_IDS = {
+ R.id.display_cutout,
+ R.id.display_cutout_left,
+ R.id.display_cutout_right,
+ R.id.display_cutout_bottom
+ };
+
private DisplayManager mDisplayManager;
@VisibleForTesting
protected boolean mIsRegistered;
@@ -139,13 +147,11 @@
protected RoundedCornerResDelegate mRoundedCornerResDelegate;
@VisibleForTesting
protected DecorProviderFactory mRoundedCornerFactory;
+ private CutoutDecorProviderFactory mCutoutFactory;
private int mProviderRefreshToken = 0;
@VisibleForTesting
protected OverlayWindow[] mOverlays = null;
@VisibleForTesting
- @Nullable
- DisplayCutoutView[] mCutoutViews;
- @VisibleForTesting
ViewGroup mScreenDecorHwcWindow;
@VisibleForTesting
ScreenDecorHwcLayer mScreenDecorHwcLayer;
@@ -187,18 +193,19 @@
return;
}
- if (mCutoutViews == null) {
- Log.w(TAG, "DisplayCutoutView not initialized onApplyCameraProtection");
- return;
- }
-
- // Show the extra protection around the front facing camera if necessary
- for (DisplayCutoutView dcv : mCutoutViews) {
- // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
- if (dcv != null) {
- dcv.setProtection(protectionPath, bounds);
- dcv.enableShowProtection(true);
+ int setProtectionCnt = 0;
+ for (int id: DISPLAY_CUTOUT_IDS) {
+ final View view = getOverlayView(id);
+ if (!(view instanceof DisplayCutoutView)) {
+ continue;
}
+ ++setProtectionCnt;
+ final DisplayCutoutView dcv = (DisplayCutoutView) view;
+ dcv.setProtection(protectionPath, bounds);
+ dcv.enableShowProtection(true);
+ }
+ if (setProtectionCnt == 0) {
+ Log.e(TAG, "CutoutView not initialized showCameraProtection");
}
}
@@ -219,16 +226,17 @@
return;
}
- if (mCutoutViews == null) {
- Log.w(TAG, "DisplayCutoutView not initialized onHideCameraProtection");
- return;
- }
- // Go back to the regular anti-aliasing
- for (DisplayCutoutView dcv : mCutoutViews) {
- // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
- if (dcv != null) {
- dcv.enableShowProtection(false);
+ int setProtectionCnt = 0;
+ for (int id: DISPLAY_CUTOUT_IDS) {
+ final View view = getOverlayView(id);
+ if (!(view instanceof DisplayCutoutView)) {
+ continue;
}
+ ++setProtectionCnt;
+ ((DisplayCutoutView) view).enableShowProtection(false);
+ }
+ if (setProtectionCnt == 0) {
+ Log.e(TAG, "CutoutView not initialized hideCameraProtection");
}
}
@@ -335,6 +343,7 @@
decorProviders.addAll(mFaceScanningFactory.getProviders());
if (!hasHwLayer) {
decorProviders.addAll(mRoundedCornerFactory.getProviders());
+ decorProviders.addAll(mCutoutFactory.getProviders());
}
return decorProviders;
}
@@ -379,6 +388,7 @@
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
getPhysicalPixelDisplaySizeRatio());
mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate);
+ mCutoutFactory = getCutoutFactory();
mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
updateHwLayerRoundedCornerDrawable();
setupDecorations();
@@ -483,18 +493,13 @@
if (needToUpdateProviderViews) {
updateOverlayProviderViews(null);
} else {
- updateOverlayProviderViews(new Integer[] { mFaceScanningViewId });
- }
-
- if (mCutoutViews != null) {
- final int size = mCutoutViews.length;
- for (int i = 0; i < size; i++) {
- final DisplayCutoutView cutoutView = mCutoutViews[i];
- if (cutoutView == null) {
- continue;
- }
- cutoutView.onDisplayChanged(newUniqueId);
- }
+ updateOverlayProviderViews(new Integer[] {
+ mFaceScanningViewId,
+ R.id.display_cutout,
+ R.id.display_cutout_left,
+ R.id.display_cutout_right,
+ R.id.display_cutout_bottom,
+ });
}
if (mScreenDecorHwcLayer != null) {
@@ -507,8 +512,9 @@
updateConfiguration();
}
+ @VisibleForTesting
@Nullable
- private View getOverlayView(@IdRes int id) {
+ View getOverlayView(@IdRes int id) {
if (mOverlays == null) {
return null;
}
@@ -565,18 +571,18 @@
removeHwcOverlay();
}
- final DisplayCutout cutout = getCutout();
+ boolean[] hasCreatedOverlay = new boolean[BOUNDS_POSITION_LENGTH];
final boolean shouldOptimizeVisibility = shouldOptimizeVisibility();
+ Integer bound;
+ while ((bound = DecorProviderKt.getProperBound(decorProviders)) != null) {
+ hasCreatedOverlay[bound] = true;
+ Pair<List<DecorProvider>, List<DecorProvider>> pair =
+ DecorProviderKt.partitionAlignedBound(decorProviders, bound);
+ decorProviders = pair.getSecond();
+ createOverlay(bound, pair.getFirst(), shouldOptimizeVisibility);
+ }
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- if (shouldShowSwLayerCutout(i, cutout)
- || shouldShowSwLayerFaceScan(i, cutout)
- || shouldShowSwLayerRoundedCorner(i, cutout)
- || shouldShowSwLayerPrivacyDot(i, cutout)) {
- Pair<List<DecorProvider>, List<DecorProvider>> pair =
- DecorProviderKt.partitionAlignedBound(decorProviders, i);
- decorProviders = pair.getSecond();
- createOverlay(i, pair.getFirst(), shouldOptimizeVisibility);
- } else {
+ if (!hasCreatedOverlay[i]) {
removeOverlay(i);
}
}
@@ -639,9 +645,10 @@
}
}
- @VisibleForTesting
- DisplayCutout getCutout() {
- return mContext.getDisplay().getCutout();
+ // For unit test to override
+ protected CutoutDecorProviderFactory getCutoutFactory() {
+ return new CutoutDecorProviderFactory(mContext.getResources(),
+ mContext.getDisplay());
}
@VisibleForTesting
@@ -731,16 +738,6 @@
overlayView.setAlpha(0);
overlayView.setForceDarkAllowed(false);
- // Only show cutout in mOverlays when hwc doesn't support screen decoration
- if (mHwcScreenDecorationSupport == null) {
- if (mCutoutViews == null) {
- mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
- }
- mCutoutViews[pos] = new DisplayCutoutView(mContext, pos);
- overlayView.addView(mCutoutViews[pos]);
- mCutoutViews[pos].updateRotation(mRotation);
- }
-
mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
overlayView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@@ -920,6 +917,7 @@
}
private void setupCameraListener() {
+ // TODO(b/238143614) Support dual screen camera protection
Resources res = mContext.getResources();
boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
if (enabled) {
@@ -948,27 +946,12 @@
mTintColor = Color.RED;
}
- if (mOverlays == null) {
- return;
- }
-
- for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- if (mOverlays[i] == null) {
- continue;
- }
- final ViewGroup overlayView = mOverlays[i].getRootView();
- final int size = overlayView.getChildCount();
- View child;
- for (int j = 0; j < size; j++) {
- child = overlayView.getChildAt(j);
- if (child instanceof DisplayCutoutView && child.getId() == R.id.display_cutout) {
- ((DisplayCutoutView) child).setColor(mTintColor);
- }
- }
- }
-
updateOverlayProviderViews(new Integer[] {
mFaceScanningViewId,
+ R.id.display_cutout,
+ R.id.display_cutout_left,
+ R.id.display_cutout_right,
+ R.id.display_cutout_bottom,
R.id.rounded_corner_top_left,
R.id.rounded_corner_top_right,
R.id.rounded_corner_bottom_left,
@@ -1093,15 +1076,6 @@
updateHwLayerRoundedCornerDrawable();
}
updateLayoutParams();
- // update cutout view rotation
- if (mCutoutViews != null) {
- for (final DisplayCutoutView cutoutView: mCutoutViews) {
- if (cutoutView == null) {
- continue;
- }
- cutoutView.updateRotation(mRotation);
- }
- }
// update all provider views inside overlay
updateOverlayProviderViews(null);
@@ -1120,46 +1094,6 @@
return mRoundedCornerFactory.getHasProviders();
}
- private boolean isDefaultShownOverlayPos(@BoundsPosition int pos,
- @Nullable DisplayCutout cutout) {
- // for cutout is null or cutout with only waterfall.
- final boolean emptyBoundsOrWaterfall = cutout == null || cutout.isBoundsEmpty();
- // Shows rounded corner on left and right overlays only when there is no top or bottom
- // cutout.
- final int rotatedTop = getBoundPositionFromRotation(BOUNDS_POSITION_TOP, mRotation);
- final int rotatedBottom = getBoundPositionFromRotation(BOUNDS_POSITION_BOTTOM, mRotation);
- if (emptyBoundsOrWaterfall || !cutout.getBoundingRectsAll()[rotatedTop].isEmpty()
- || !cutout.getBoundingRectsAll()[rotatedBottom].isEmpty()) {
- return pos == BOUNDS_POSITION_TOP || pos == BOUNDS_POSITION_BOTTOM;
- } else {
- return pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_RIGHT;
- }
- }
-
- private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos,
- @Nullable DisplayCutout cutout) {
- return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout)
- && mHwcScreenDecorationSupport == null;
- }
-
- private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos,
- @Nullable DisplayCutout cutout) {
- return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout);
- }
-
- private boolean shouldShowSwLayerFaceScan(@BoundsPosition int pos,
- @Nullable DisplayCutout cutout) {
- return mFaceScanningFactory.getHasProviders() && isDefaultShownOverlayPos(pos, cutout);
- }
-
- private boolean shouldShowSwLayerCutout(@BoundsPosition int pos,
- @Nullable DisplayCutout cutout) {
- final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
- final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
- return (bounds != null && !bounds[rotatedPos].isEmpty()
- && mHwcScreenDecorationSupport == null);
- }
-
private boolean shouldOptimizeVisibility() {
return (isPrivacyDotEnabled() || mFaceScanningFactory.getHasProviders())
&& (mHwcScreenDecorationSupport != null
@@ -1168,7 +1102,7 @@
}
private boolean shouldDrawCutout() {
- return shouldDrawCutout(mContext);
+ return mCutoutFactory.getHasProviders();
}
static boolean shouldDrawCutout(Context context) {
@@ -1284,7 +1218,6 @@
paint.setColor(mColor);
paint.setStyle(Paint.Style.FILL);
- setId(R.id.display_cutout);
if (DEBUG) {
getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
getWindowTitleByPos(pos) + " drawn in rot " + mRotation));
@@ -1292,6 +1225,9 @@
}
public void setColor(int color) {
+ if (color == mColor) {
+ return;
+ }
mColor = color;
paint.setColor(mColor);
invalidate();
@@ -1299,6 +1235,12 @@
@Override
public void updateRotation(int rotation) {
+ // updateRotation() is called inside CutoutDecorProviderImpl::onReloadResAndMeasure()
+ // during onDisplayChanged. In order to prevent reloading cutout info in super class,
+ // check mRotation at first
+ if (rotation == mRotation) {
+ return;
+ }
mRotation = rotation;
super.updateRotation(rotation);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 84e1c3d..86837366 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -129,6 +129,8 @@
private final Set<Integer> mFailedModalities = new HashSet<Integer>();
private final @Background DelayableExecutor mBackgroundExecutor;
+ private int mOrientation;
+ private boolean mSkipFirstLostFocus = false;
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
@@ -441,6 +443,12 @@
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
+ //it's a workaround to avoid closing BP incorrectly
+ //BP gets a onWindowFocusChanged(false) and then gets a onWindowFocusChanged(true)
+ if (mSkipFirstLostFocus) {
+ mSkipFirstLostFocus = false;
+ return;
+ }
Log.v(TAG, "Lost window focus, dismissing the dialog");
animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
}
@@ -450,6 +458,9 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
+ //save the first orientation
+ mOrientation = getResources().getConfiguration().orientation;
+
mWakefulnessLifecycle.addObserver(this);
if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
@@ -621,6 +632,12 @@
if (mBiometricView != null) {
mBiometricView.restoreState(savedState);
}
+
+ if (savedState != null) {
+ mSkipFirstLostFocus = savedState.getBoolean(
+ AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED);
+ }
+
wm.addView(this, getLayoutParams(mWindowToken, mConfig.mPromptInfo.getTitle()));
}
@@ -677,6 +694,10 @@
mBiometricView != null && mCredentialView == null);
outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null);
+ if (mOrientation != getResources().getConfiguration().orientation) {
+ outState.putBoolean(AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED, true);
+ }
+
if (mBiometricView != null) {
mBiometricView.onSaveState(outState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 282f251..6b3f134 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -19,6 +19,9 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -599,7 +602,6 @@
@Background DelayableExecutor bgExecutor) {
super(context);
mExecution = execution;
- mWakefulnessLifecycle = wakefulnessLifecycle;
mUserManager = userManager;
mLockPatternUtils = lockPatternUtils;
mHandler = handler;
@@ -625,11 +627,24 @@
return Unit.INSTANCE;
});
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onFinishedWakingUp() {
+ notifyDozeChanged(mStatusBarStateController.isDozing(), WAKEFULNESS_AWAKE);
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ notifyDozeChanged(mStatusBarStateController.isDozing(), WAKEFULNESS_GOING_TO_SLEEP);
+ }
+ });
+
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- notifyDozeChanged(isDozing);
+ notifyDozeChanged(isDozing, wakefulnessLifecycle.getWakefulness());
}
});
@@ -738,13 +753,16 @@
@Override
public void setBiometicContextListener(IBiometricContextListener listener) {
mBiometricContextListener = listener;
- notifyDozeChanged(mStatusBarStateController.isDozing());
+ notifyDozeChanged(mStatusBarStateController.isDozing(),
+ mWakefulnessLifecycle.getWakefulness());
}
- private void notifyDozeChanged(boolean isDozing) {
+ private void notifyDozeChanged(boolean isDozing,
+ @WakefulnessLifecycle.Wakefulness int wakefullness) {
if (mBiometricContextListener != null) {
try {
- mBiometricContextListener.onDozeChanged(isDozing);
+ final boolean isAwake = wakefullness == WAKEFULNESS_AWAKE;
+ mBiometricContextListener.onDozeChanged(isDozing, isAwake);
} catch (RemoteException e) {
Log.w(TAG, "failed to notify initial doze state");
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index 51f39b3..cd0fc37 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -48,6 +48,8 @@
String KEY_BIOMETRIC_SENSOR_TYPE = "sensor_type";
String KEY_BIOMETRIC_SENSOR_PROPS = "sensor_props";
+ String KEY_BIOMETRIC_ORIENTATION_CHANGED = "orientation_changed";
+
int SIZE_UNKNOWN = 0;
/**
* Minimal UI, showing only biometric icon.
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index 4fe2dd8..e2ef247 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -33,7 +33,6 @@
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -117,7 +116,7 @@
)
} catch (e: RemoteException) {
Log.w(
- NotificationPanelViewController.TAG,
+ "CameraGestureHelper",
"Unable to start camera activity",
e
)
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index da675de..dec3d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -200,8 +200,7 @@
}
private fun updateRippleColor() {
- rippleView.setColor(
- Utils.getColorAttr(context, android.R.attr.colorAccent).defaultColor)
+ rippleView.setColor(Utils.getColorAttr(context, android.R.attr.colorAccent).defaultColor)
}
inner class ChargingRippleCommand : Command {
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 0839338..c0cc6b4 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -30,13 +30,12 @@
import android.widget.ImageView;
import android.widget.TextView;
-import androidx.core.graphics.ColorUtils;
-
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.ripple.RippleView;
+import com.android.systemui.ripple.RippleViewKt;
import java.text.NumberFormat;
@@ -143,16 +142,15 @@
mRippleView = findViewById(R.id.wireless_charging_ripple);
mRippleView.setupShader(rippleShape);
+ int color = Utils.getColorAttr(mRippleView.getContext(),
+ android.R.attr.colorAccent).getDefaultColor();
if (mRippleView.getRippleShape() == RippleShape.ROUNDED_BOX) {
mRippleView.setDuration(ROUNDED_BOX_RIPPLE_ANIMATION_DURATION);
mRippleView.setSparkleStrength(0.22f);
- int color = Utils.getColorAttr(mRippleView.getContext(),
- android.R.attr.colorAccent).getDefaultColor();
- mRippleView.setColor(ColorUtils.setAlphaComponent(color, 28));
+ mRippleView.setColor(color, 28);
} else {
mRippleView.setDuration(CIRCLE_RIPPLE_ANIMATION_DURATION);
- mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
- android.R.attr.colorAccent).getDefaultColor());
+ mRippleView.setColor(color, RippleViewKt.RIPPLE_DEFAULT_ALPHA);
}
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 8ba6f1c..d60a222 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -26,6 +26,7 @@
import com.android.systemui.screenshot.ActionProxyReceiver;
import com.android.systemui.screenshot.DeleteScreenshotReceiver;
import com.android.systemui.screenshot.SmartActionsReceiver;
+import com.android.systemui.volume.VolumePanelDialogReceiver;
import dagger.Binds;
import dagger.Module;
@@ -78,6 +79,15 @@
*/
@Binds
@IntoMap
+ @ClassKey(VolumePanelDialogReceiver.class)
+ public abstract BroadcastReceiver bindVolumePanelDialogReceiver(
+ VolumePanelDialogReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
@ClassKey(PeopleSpaceWidgetPinnedReceiver.class)
public abstract BroadcastReceiver bindPeopleSpaceWidgetPinnedReceiver(
PeopleSpaceWidgetPinnedReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 6db3e82..8bb27a7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.dagger
import com.android.keyguard.KeyguardBiometricLockoutLogger
+import com.android.systemui.ChooserSelector
import com.android.systemui.CoreStartable
import com.android.systemui.LatencyTester
import com.android.systemui.ScreenDecorations
@@ -60,6 +61,12 @@
@ClassKey(AuthController::class)
abstract fun bindAuthController(service: AuthController): CoreStartable
+ /** Inject into ChooserCoreStartable. */
+ @Binds
+ @IntoMap
+ @ClassKey(ChooserSelector::class)
+ abstract fun bindChooserSelector(sysui: ChooserSelector): CoreStartable
+
/** Inject into ClipboardListener. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1b060e2..a0ecd22 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -65,7 +65,6 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
@@ -225,11 +224,9 @@
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
- NotificationGroupManagerLegacy groupManager,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
- DumpManager dumpManager,
@Main Executor sysuiMainExecutor) {
return Optional.ofNullable(BubblesManager.create(context,
bubblesOptional,
@@ -243,7 +240,6 @@
interruptionStateProvider,
zenModeController,
notifUserManager,
- groupManager,
notifCollection,
notifPipeline,
sysUiState,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt
new file mode 100644
index 0000000..cbed21c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.systemui.decor
+
+import android.content.res.Resources
+import android.util.Log
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+
+class CutoutDecorProviderFactory constructor(
+ private val res: Resources,
+ private val display: Display?,
+) : DecorProviderFactory() {
+
+ val displayInfo = DisplayInfo()
+
+ override val hasProviders: Boolean
+ get() {
+ display?.getDisplayInfo(displayInfo) ?: run {
+ Log.w(TAG, "display is null, can't update displayInfo")
+ }
+ return DisplayCutout.getFillBuiltInDisplayCutout(res, displayInfo.uniqueId)
+ }
+
+ override val providers: List<DecorProvider>
+ get() {
+ if (!hasProviders) {
+ return emptyList()
+ }
+
+ return ArrayList<DecorProvider>().also { list ->
+ // We need to update displayInfo before using it, but it has already updated during
+ // accessing hasProviders field
+ displayInfo.displayCutout?.getBoundBaseOnCurrentRotation()?.let { bounds ->
+ for (bound in bounds) {
+ list.add(
+ CutoutDecorProviderImpl(bound.baseOnRotation0(displayInfo.rotation))
+ )
+ }
+ }
+ }
+ }
+}
+
+private const val TAG = "CutoutDecorProviderFactory"
diff --git a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
new file mode 100644
index 0000000..991b54e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
@@ -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 com.android.systemui.decor
+
+import android.content.Context
+import android.view.DisplayCutout
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.ScreenDecorations.DisplayCutoutView
+
+class CutoutDecorProviderImpl(
+ @DisplayCutout.BoundsPosition override val alignedBound: Int
+) : BoundDecorProvider() {
+
+ override val viewId: Int = when (alignedBound) {
+ DisplayCutout.BOUNDS_POSITION_TOP -> R.id.display_cutout
+ DisplayCutout.BOUNDS_POSITION_LEFT -> R.id.display_cutout_left
+ DisplayCutout.BOUNDS_POSITION_RIGHT -> R.id.display_cutout_right
+ else -> R.id.display_cutout_bottom
+ }
+
+ override fun inflateView(
+ context: Context,
+ parent: ViewGroup,
+ @Surface.Rotation rotation: Int,
+ tintColor: Int
+ ): View {
+ return DisplayCutoutView(context, alignedBound).also { view ->
+ view.id = viewId
+ view.setColor(tintColor)
+ parent.addView(view)
+ view.updateRotation(rotation)
+ }
+ }
+
+ override fun onReloadResAndMeasure(
+ view: View,
+ reloadToken: Int,
+ @Surface.Rotation rotation: Int,
+ tintColor: Int,
+ displayUniqueId: String?
+ ) {
+ (view as? DisplayCutoutView)?.let { cutoutView ->
+ cutoutView.setColor(tintColor)
+ cutoutView.updateRotation(rotation)
+ cutoutView.onDisplayChanged(displayUniqueId)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
index de6d727..0681f50 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
@@ -32,7 +32,7 @@
abstract val viewId: Int
/** The number of total aligned bounds */
- val numOfAlignedEdge: Int
+ val numOfAlignedBound: Int
get() = alignedBounds.size
/** The aligned bounds for the view which is created through inflateView() */
@@ -57,16 +57,8 @@
@Surface.Rotation rotation: Int,
tintColor: Int
): View
-}
-/**
- * Split list to 2 list, and return it back as Pair<>. The providers on the first list contains this
- * alignedBound element. The providers on the second list do not contain this alignedBound element
- */
-fun List<DecorProvider>.partitionAlignedBound(
- @DisplayCutout.BoundsPosition alignedBound: Int
-): Pair<List<DecorProvider>, List<DecorProvider>> {
- return partition { it.alignedBounds.contains(alignedBound) }
+ override fun toString() = "${javaClass.simpleName}{alignedBounds=$alignedBounds}"
}
/**
@@ -94,3 +86,61 @@
listOf(alignedBound)
}
}
+
+/**
+ * Split list to 2 sub-lists, and return it back as Pair<>. The providers on the first list contains
+ * this alignedBound element. The providers on the second list do not contain this alignedBound
+ * element.
+ */
+fun List<DecorProvider>.partitionAlignedBound(
+ @DisplayCutout.BoundsPosition alignedBound: Int
+): Pair<List<DecorProvider>, List<DecorProvider>> {
+ return partition { it.alignedBounds.contains(alignedBound) }
+}
+
+/**
+ * Get the proper bound from DecorProvider list
+ * Time complexity: O(N), N is the number of providers
+ *
+ * Choose order
+ * 1. Return null if list is empty
+ * 2. If list contains BoundDecorProvider, return its alignedBound[0] because it is a must-have
+ * bound
+ * 3. Return the bound with most DecorProviders
+ */
+fun List<DecorProvider>.getProperBound(): Int? {
+ // Return null if list is empty
+ if (isEmpty()) {
+ return null
+ }
+
+ // Choose alignedBounds[0] of BoundDecorProvider if any
+ val singleBoundProvider = firstOrNull { it.numOfAlignedBound == 1 }
+ if (singleBoundProvider != null) {
+ return singleBoundProvider.alignedBounds[0]
+ }
+
+ // Return the bound with most DecorProviders
+ val boundCount = intArrayOf(0, 0, 0, 0)
+ for (provider in this) {
+ for (bound in provider.alignedBounds) {
+ boundCount[bound]++
+ }
+ }
+ var maxCount = 0
+ var maxCountBound: Int? = null
+ val bounds = arrayOf(
+ // Put top and bottom at first to get the highest priority to be chosen
+ DisplayCutout.BOUNDS_POSITION_TOP,
+ DisplayCutout.BOUNDS_POSITION_BOTTOM,
+ DisplayCutout.BOUNDS_POSITION_LEFT,
+ DisplayCutout.BOUNDS_POSITION_RIGHT
+ )
+ for (bound in bounds) {
+ if (boundCount[bound] > maxCount) {
+ maxCountBound = bound
+ maxCount = boundCount[bound]
+ }
+ }
+ return maxCountBound
+}
diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
index dfb0b5a..45b8a08 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
@@ -114,7 +114,8 @@
pw.println(" rootView=$rootView")
for (i in 0 until rootView.childCount) {
val child = rootView.getChildAt(i)
- pw.println(" child[$i]=$child")
+ val provider = viewProviderMap[child.id]?.second
+ pw.println(" child[$i]=$child $provider")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
index d5db63d..75a97de 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -35,7 +35,7 @@
public class ComplicationUtils {
/**
* Converts a {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to
- * {@link ComplicationType}.
+ * {@link Complication.ComplicationType}.
*/
@Complication.ComplicationType
public static int convertComplicationType(@DreamBackend.ComplicationType int type) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index 1c72e49..2503d3c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -151,8 +151,8 @@
* Controls behavior of the dream complication.
*/
static class DreamHomeControlsChipViewController extends ViewController<ImageView> {
- private static final boolean DEBUG = false;
private static final String TAG = "DreamHomeControlsCtrl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ActivityStarter mActivityStarter;
private final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
new file mode 100644
index 0000000..21a51d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
@@ -0,0 +1,135 @@
+/*
+ * 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.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent.DreamMediaEntryModule.DREAM_MEDIA_ENTRY_VIEW;
+import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_MEDIA_ENTRY_LAYOUT_PARAMS;
+
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent;
+import com.android.systemui.media.dream.MediaDreamComplication;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A dream complication that shows a media entry chip to launch media control view.
+ */
+public class DreamMediaEntryComplication implements Complication {
+ private final DreamMediaEntryComplicationComponent.Factory mComponentFactory;
+
+ @Inject
+ public DreamMediaEntryComplication(
+ DreamMediaEntryComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+
+ /**
+ * Contains values/logic associated with the dream complication view.
+ */
+ public static class DreamMediaEntryViewHolder implements ViewHolder {
+ private final View mView;
+ private final ComplicationLayoutParams mLayoutParams;
+ private final DreamMediaEntryViewController mViewController;
+
+ @Inject
+ DreamMediaEntryViewHolder(
+ DreamMediaEntryViewController dreamMediaEntryViewController,
+ @Named(DREAM_MEDIA_ENTRY_VIEW) View view,
+ @Named(DREAM_MEDIA_ENTRY_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
+ ) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ mViewController = dreamMediaEntryViewController;
+ mViewController.init();
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+
+ /**
+ * Controls behavior of the dream complication.
+ */
+ static class DreamMediaEntryViewController extends ViewController<View> {
+ private static final String TAG = "DreamMediaEntryVwCtrl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final MediaDreamComplication mMediaComplication;
+
+ private boolean mMediaComplicationAdded;
+
+ @Inject
+ DreamMediaEntryViewController(
+ @Named(DREAM_MEDIA_ENTRY_VIEW) View view,
+ DreamOverlayStateController dreamOverlayStateController,
+ MediaDreamComplication mediaComplication) {
+ super(view);
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mMediaComplication = mediaComplication;
+ mView.setOnClickListener(this::onClickMediaEntry);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ }
+
+ @Override
+ protected void onViewDetached() {
+ removeMediaComplication();
+ }
+
+ private void onClickMediaEntry(View v) {
+ if (DEBUG) Log.d(TAG, "media entry complication tapped");
+
+ if (!mMediaComplicationAdded) {
+ addMediaComplication();
+ } else {
+ removeMediaComplication();
+ }
+ }
+
+ private void addMediaComplication() {
+ mView.setSelected(true);
+ mDreamOverlayStateController.addComplication(mMediaComplication);
+ mMediaComplicationAdded = true;
+ }
+
+ private void removeMediaComplication() {
+ mView.setSelected(false);
+ mDreamOverlayStateController.removeComplication(mMediaComplication);
+ mMediaComplicationAdded = false;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index 567bdbc..a981f25 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -70,11 +70,7 @@
new BcSmartspaceDataPlugin.SmartspaceTargetListener() {
@Override
public void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets) {
- if (!targets.isEmpty()) {
- mDreamOverlayStateController.addComplication(mComplication);
- } else {
- mDreamOverlayStateController.removeComplication(mComplication);
- }
+ mDreamOverlayStateController.addComplication(mComplication);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamMediaEntryComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamMediaEntryComplicationComponent.java
new file mode 100644
index 0000000..ed05daf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamMediaEntryComplicationComponent.java
@@ -0,0 +1,81 @@
+/*
+ * 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.systemui.dreams.complication.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.DreamMediaEntryComplication;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Responsible for generating dependencies for the {@link DreamMediaEntryComplication}.
+ */
+@Subcomponent(modules = DreamMediaEntryComplicationComponent.DreamMediaEntryModule.class)
+@DreamMediaEntryComplicationComponent.DreamMediaEntryComplicationScope
+public interface DreamMediaEntryComplicationComponent {
+ /**
+ * Creates a view holder for the media entry complication.
+ */
+ DreamMediaEntryComplication.DreamMediaEntryViewHolder getViewHolder();
+
+ /**
+ * Scope of the media entry complication.
+ */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamMediaEntryComplicationScope {}
+
+ /**
+ * Factory that generates a {@link DreamMediaEntryComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamMediaEntryComplicationComponent create();
+ }
+
+ /**
+ * Scoped injected values for the {@link DreamMediaEntryComplicationComponent}.
+ */
+ @Module
+ interface DreamMediaEntryModule {
+ String DREAM_MEDIA_ENTRY_VIEW = "dream_media_entry_view";
+
+ /**
+ * Provides the dream media entry view.
+ */
+ @Provides
+ @DreamMediaEntryComplicationScope
+ @Named(DREAM_MEDIA_ENTRY_VIEW)
+ static View provideMediaEntryView(LayoutInflater layoutInflater) {
+ return (View) layoutInflater.inflate(R.layout.dream_overlay_media_entry_chip, null);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index eb07238..759d6ec 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -38,15 +38,19 @@
},
subcomponents = {
DreamHomeControlsComplicationComponent.class,
+ DreamMediaEntryComplicationComponent.class
})
public interface RegisteredComplicationsModule {
String DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS = "time_complication_layout_params";
String DREAM_SMARTSPACE_LAYOUT_PARAMS = "smartspace_layout_params";
String DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS = "home_controls_chip_layout_params";
+ String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 0;
+ int DREAM_MEDIA_COMPLICATION_WEIGHT = -1;
int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 1;
+ int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 0;
/**
* Provides layout parameters for the clock time complication.
@@ -78,6 +82,21 @@
}
/**
+ * Provides layout parameters for the media entry complication.
+ */
+ @Provides
+ @Named(DREAM_MEDIA_ENTRY_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideMediaEntryLayoutParams(@Main Resources res) {
+ return new ComplicationLayoutParams(
+ res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
+ res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_END,
+ DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT);
+ }
+
+ /**
* Provides layout parameters for the smartspace complication.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 45237a3..c4553c4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -252,6 +252,9 @@
// 1400 - columbus, b/242800729
public static final UnreleasedFlag QUICK_TAP_IN_PCC = new UnreleasedFlag(1400);
+ // 1500 - chooser
+ public static final UnreleasedFlag CHOOSER_UNBUNDLED = new UnreleasedFlag(1500);
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 9e2b7c7..a3dc779 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -50,7 +50,6 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -61,6 +60,7 @@
import java.util.Set;
import javax.inject.Inject;
+import javax.inject.Provider;
/** */
@SysUISingleton
@@ -106,6 +106,8 @@
protected volatile Context mContext;
+ private final Provider<LocalBluetoothManager> mBluetoothManagerProvider;
+
private boolean mEnabled;
private String mKeyboardName;
private CachedBluetoothDeviceManager mCachedDeviceManager;
@@ -122,8 +124,9 @@
private int mState;
@Inject
- public KeyboardUI(Context context) {
+ public KeyboardUI(Context context, Provider<LocalBluetoothManager> bluetoothManagerProvider) {
super(context);
+ this.mBluetoothManagerProvider = bluetoothManagerProvider;
}
@Override
@@ -181,7 +184,7 @@
return;
}
- LocalBluetoothManager bluetoothManager = Dependency.get(LocalBluetoothManager.class);
+ LocalBluetoothManager bluetoothManager = mBluetoothManagerProvider.get();
if (bluetoothManager == null) {
if (DEBUG) {
Slog.e(TAG, "Failed to retrieve LocalBluetoothManager instance");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 70e0d5f..2c60d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -73,6 +73,8 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.IRemoteAnimationFinishedCallback;
@@ -104,7 +106,6 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.logging.KeyguardViewMediatorLogger;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
@@ -512,7 +513,8 @@
public void onKeyguardVisibilityChanged(boolean showing) {
synchronized (KeyguardViewMediator.this) {
if (!showing && mPendingPinLock) {
- mLogger.logPinLockRequestedStartingKeyguard();
+ Log.i(TAG, "PIN lock requested, starting keyguard");
+
// Bring the keyguard back in order to show the PIN lock
mPendingPinLock = false;
doKeyguardLocked(null);
@@ -522,7 +524,7 @@
@Override
public void onUserSwitching(int userId) {
- mLogger.logUserSwitching(userId);
+ if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
// Note that the mLockPatternUtils user has already been updated from setCurrentUser.
// We need to force a reset of the views, since lockNow (called by
// ActivityManagerService) will not reconstruct the keyguard if it is already showing.
@@ -540,7 +542,7 @@
@Override
public void onUserSwitchComplete(int userId) {
- mLogger.logOnUserSwitchComplete(userId);
+ if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
if (userId != UserHandle.USER_SYSTEM) {
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
// Don't try to dismiss if the user has Pin/Pattern/Password set
@@ -568,7 +570,8 @@
public void onSimStateChanged(int subId, int slotId, int simState) {
if (DEBUG_SIM_STATES) {
- mLogger.logOnSimStateChanged(subId, slotId, String.valueOf(simState));
+ Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId
+ + ",state=" + simState + ")");
}
int size = mKeyguardStateCallbacks.size();
@@ -577,7 +580,7 @@
try {
mKeyguardStateCallbacks.get(i).onSimSecureStateChanged(simPinSecure);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnSimSecureStateChanged(e);
+ Slog.w(TAG, "Failed to call onSimSecureStateChanged", e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(i);
}
@@ -600,9 +603,9 @@
synchronized (KeyguardViewMediator.this) {
if (shouldWaitForProvisioning()) {
if (!mShowing) {
- if (DEBUG_SIM_STATES) {
- mLogger.logIccAbsentIsNotShowing();
- }
+ if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing,"
+ + " we need to show the keyguard since the "
+ + "device isn't provisioned yet.");
doKeyguardLocked(null);
} else {
resetStateLocked();
@@ -612,9 +615,8 @@
// MVNO SIMs can become transiently NOT_READY when switching networks,
// so we should only lock when they are ABSENT.
if (lastSimStateWasLocked) {
- if (DEBUG_SIM_STATES) {
- mLogger.logSimMovedToAbsent();
- }
+ if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+ + "previous state was locked. Reset the state.");
resetStateLocked();
}
mSimWasLocked.append(slotId, false);
@@ -627,9 +629,9 @@
mSimWasLocked.append(slotId, true);
mPendingPinLock = true;
if (!mShowing) {
- if (DEBUG_SIM_STATES) {
- mLogger.logIntentValueIccLocked();
- }
+ if (DEBUG_SIM_STATES) Log.d(TAG,
+ "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
+ + "showing; need to show keyguard so user can enter sim pin");
doKeyguardLocked(null);
} else {
resetStateLocked();
@@ -639,36 +641,29 @@
case TelephonyManager.SIM_STATE_PERM_DISABLED:
synchronized (KeyguardViewMediator.this) {
if (!mShowing) {
- if (DEBUG_SIM_STATES) {
- mLogger.logPermDisabledKeyguardNotShowing();
- }
+ if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED and "
+ + "keygaurd isn't showing.");
doKeyguardLocked(null);
} else {
- if (DEBUG_SIM_STATES) {
- mLogger.logPermDisabledResetStateLocked();
- }
+ if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+ + "show permanently disabled message in lockscreen.");
resetStateLocked();
}
}
break;
case TelephonyManager.SIM_STATE_READY:
synchronized (KeyguardViewMediator.this) {
- if (DEBUG_SIM_STATES) {
- mLogger.logReadyResetState(mShowing);
- }
+ if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
if (mShowing && mSimWasLocked.get(slotId, false)) {
- if (DEBUG_SIM_STATES) {
- mLogger.logSimMovedToReady();
- }
+ if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
+ + "previously was locked. Reset the state.");
mSimWasLocked.append(slotId, false);
resetStateLocked();
}
}
break;
default:
- if (DEBUG_SIM_STATES) {
- mLogger.logUnspecifiedSimState(simState);
- }
+ if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState);
break;
}
}
@@ -713,7 +708,7 @@
if (targetUserId != ActivityManager.getCurrentUser()) {
return;
}
- mLogger.logKeyguardDone();
+ if (DEBUG) Log.d(TAG, "keyguardDone");
tryKeyguardDone();
}
@@ -732,7 +727,7 @@
@Override
public void keyguardDonePending(boolean strongAuth, int targetUserId) {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
- mLogger.logKeyguardDonePending();
+ if (DEBUG) Log.d(TAG, "keyguardDonePending");
if (targetUserId != ActivityManager.getCurrentUser()) {
Trace.endSection();
return;
@@ -751,7 +746,7 @@
@Override
public void keyguardGone() {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardGone");
- mLogger.logKeyguardGone();
+ if (DEBUG) Log.d(TAG, "keyguardGone");
mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false);
mKeyguardDisplayManager.hide();
Trace.endSection();
@@ -837,7 +832,8 @@
@Override
public void onLaunchAnimationCancelled() {
- mLogger.logOccludeLaunchAnimationCancelled(mOccluded);
+ Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: "
+ + mOccluded);
}
@Override
@@ -857,7 +853,8 @@
@Override
public void setLaunchContainer(@NonNull ViewGroup launchContainer) {
// No-op, launch container is always the shade.
- mLogger.logActivityLaunchAnimatorLaunchContainerChanged();
+ Log.wtf(TAG, "Someone tried to change the launch container for the "
+ + "ActivityLaunchAnimator, which should never happen.");
}
@NonNull
@@ -908,7 +905,8 @@
}
setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
- mLogger.logUnoccludeAnimationCancelled(mOccluded);
+ Log.d(TAG, "Unocclude animation cancelled. Occluded state is now: "
+ + mOccluded);
}
@Override
@@ -916,11 +914,12 @@
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
- mLogger.logUnoccludeAnimatorOnAnimationStart();
+ Log.d(TAG, "UnoccludeAnimator#onAnimationStart. Set occluded = false.");
setOccluded(false /* isOccluded */, true /* animate */);
if (apps == null || apps.length == 0 || apps[0] == null) {
- mLogger.logNoAppsProvidedToUnoccludeRunner();
+ Log.d(TAG, "No apps provided to unocclude runner; "
+ + "skipping animation and unoccluding.");
finishedCallback.onAnimationFinished();
return;
}
@@ -1008,7 +1007,6 @@
private ScreenOnCoordinator mScreenOnCoordinator;
private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
- private KeyguardViewMediatorLogger mLogger;
/**
* Injected constructor. See {@link KeyguardModule}.
@@ -1037,8 +1035,7 @@
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
- KeyguardViewMediatorLogger logger) {
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
super(context);
mFalsingCollector = falsingCollector;
mLockPatternUtils = lockPatternUtils;
@@ -1081,7 +1078,6 @@
mDreamOverlayStateController = dreamOverlayStateController;
mActivityLaunchAnimator = activityLaunchAnimator;
- mLogger = logger;
mPowerButtonY = context.getResources().getDimensionPixelSize(
R.dimen.physical_power_button_center_screen_location_y);
@@ -1145,21 +1141,21 @@
mLockSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mLockSoundId == 0) {
- mLogger.logFailedLoadLockSound(soundPath);
+ Log.w(TAG, "failed to load lock sound from " + soundPath);
}
soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
if (soundPath != null) {
mUnlockSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mUnlockSoundId == 0) {
- mLogger.logFailedLoadUnlockSound(soundPath);
+ Log.w(TAG, "failed to load unlock sound from " + soundPath);
}
soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND);
if (soundPath != null) {
mTrustedSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mTrustedSoundId == 0) {
- mLogger.logFailedLoadTrustedSound(soundPath);
+ Log.w(TAG, "failed to load trusted sound from " + soundPath);
}
int lockSoundDefaultAttenuation = mContext.getResources().getInteger(
@@ -1188,7 +1184,7 @@
private void handleSystemReady() {
synchronized (this) {
- mLogger.logOnSystemReady();
+ if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
doKeyguardLocked(null);
mUpdateMonitor.registerCallback(mUpdateCallback);
@@ -1206,7 +1202,7 @@
* {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_TIMEOUT}.
*/
public void onStartedGoingToSleep(@WindowManagerPolicyConstants.OffReason int offReason) {
- mLogger.logOnStartedGoingToSleep(offReason);
+ if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + offReason + ")");
synchronized (this) {
mDeviceInteractive = false;
mPowerGestureIntercepted = false;
@@ -1222,11 +1218,11 @@
long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
mLockLater = false;
if (mExitSecureCallback != null) {
- mLogger.logPendingExitSecureCallbackCancelled();
+ if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
try {
mExitSecureCallback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- mLogger.logFailedOnKeyguardExitResultFalse(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
mExitSecureCallback = null;
if (!mExternallyEnabled) {
@@ -1271,7 +1267,7 @@
*/
public void onFinishedGoingToSleep(
@WindowManagerPolicyConstants.OffReason int offReason, boolean cameraGestureTriggered) {
- mLogger.logOnFinishedGoingToSleep(offReason);
+ if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + offReason + ")");
synchronized (this) {
mDeviceInteractive = false;
mGoingToSleep = false;
@@ -1329,7 +1325,13 @@
// - The screen off animation is cancelled by the device waking back up. We will call
// maybeHandlePendingLock from KeyguardViewMediator#onStartedWakingUp.
if (mScreenOffAnimationController.isKeyguardShowDelayed()) {
- mLogger.logMaybeHandlePendingLockNotHandling();
+ if (DEBUG) {
+ Log.d(TAG, "#maybeHandlePendingLock: not handling because the screen off "
+ + "animation's isKeyguardShowDelayed() returned true. This should be "
+ + "handled soon by #onStartedWakingUp, or by the end actions of the "
+ + "screen off animation.");
+ }
+
return;
}
@@ -1339,11 +1341,18 @@
// StatusBar#finishKeyguardFadingAway, which is always responsible for setting
// isKeyguardGoingAway to false.
if (mKeyguardStateController.isKeyguardGoingAway()) {
- mLogger.logMaybeHandlePendingLockKeyguardGoingAway();
+ if (DEBUG) {
+ Log.d(TAG, "#maybeHandlePendingLock: not handling because the keyguard is "
+ + "going away. This should be handled shortly by "
+ + "StatusBar#finishKeyguardFadingAway.");
+ }
+
return;
}
- mLogger.logMaybeHandlePendingLockHandling();
+ if (DEBUG) {
+ Log.d(TAG, "#maybeHandlePendingLock: handling pending lock; locking keyguard.");
+ }
doKeyguardLocked(null);
setPendingLock(false);
@@ -1412,7 +1421,8 @@
PendingIntent sender = PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
- mLogger.logSetAlarmToTurnOffKeyguard(mDelayedShowingSequence);
+ if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+ + mDelayedShowingSequence);
doKeyguardLaterForChildProfilesLocked();
}
@@ -1472,7 +1482,7 @@
mAnimatingScreenOff = false;
cancelDoKeyguardLaterLocked();
cancelDoKeyguardForChildProfilesLocked();
- mLogger.logOnStartedWakingUp(mDelayedShowingSequence);
+ if (DEBUG) Log.d(TAG, "onStartedWakingUp, seq = " + mDelayedShowingSequence);
notifyStartedWakingUp();
}
mUpdateMonitor.dispatchStartedWakingUp();
@@ -1532,35 +1542,37 @@
*/
public void setKeyguardEnabled(boolean enabled) {
synchronized (this) {
- mLogger.logSetKeyguardEnabled(enabled);
+ if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
mExternallyEnabled = enabled;
if (!enabled && mShowing) {
if (mExitSecureCallback != null) {
- mLogger.logIgnoreVerifyUnlockRequest();
+ if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
// we're in the process of handling a request to verify the user
// can get past the keyguard. ignore extraneous requests to disable / re-enable
return;
}
// hiding keyguard that is showing, remember to reshow later
- mLogger.logRememberToReshowLater();
+ if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
+ + "disabling status bar expansion");
mNeedToReshowWhenReenabled = true;
updateInputRestrictedLocked();
hideLocked();
} else if (enabled && mNeedToReshowWhenReenabled) {
// re-enabled after previously hidden, reshow
- mLogger.logPreviouslyHiddenReshow();
+ if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
+ + "status bar expansion");
mNeedToReshowWhenReenabled = false;
updateInputRestrictedLocked();
if (mExitSecureCallback != null) {
- mLogger.logOnKeyguardExitResultFalseResetting();
+ if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting");
try {
mExitSecureCallback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
mExitSecureCallback = null;
resetStateLocked();
@@ -1572,7 +1584,7 @@
// and causing an ANR).
mWaitingUntilKeyguardVisible = true;
mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
- mLogger.logWaitingUntilKeyguardVisibleIsFalse();
+ if (DEBUG) Log.d(TAG, "waiting until mWaitingUntilKeyguardVisible is false");
while (mWaitingUntilKeyguardVisible) {
try {
wait();
@@ -1580,7 +1592,7 @@
Thread.currentThread().interrupt();
}
}
- mLogger.logDoneWaitingUntilKeyguardVisible();
+ if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible");
}
}
}
@@ -1592,31 +1604,31 @@
public void verifyUnlock(IKeyguardExitCallback callback) {
Trace.beginSection("KeyguardViewMediator#verifyUnlock");
synchronized (this) {
- mLogger.logVerifyUnlock();
+ if (DEBUG) Log.d(TAG, "verifyUnlock");
if (shouldWaitForProvisioning()) {
// don't allow this api when the device isn't provisioned
- mLogger.logIgnoreUnlockDeviceNotProvisioned();
+ if (DEBUG) Log.d(TAG, "ignoring because device isn't provisioned");
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
} else if (mExternallyEnabled) {
// this only applies when the user has externally disabled the
// keyguard. this is unexpected and means the user is not
// using the api properly.
- mLogger.logVerifyUnlockCalledNotExternallyDisabled();
+ Log.w(TAG, "verifyUnlock called when not externally disabled");
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
} else if (mExitSecureCallback != null) {
// already in progress with someone else
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
} else if (!isSecure()) {
@@ -1628,7 +1640,7 @@
try {
callback.onKeyguardExitResult(true);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
} else {
@@ -1637,7 +1649,7 @@
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
}
}
}
@@ -1655,8 +1667,10 @@
* Notify us when the keyguard is occluded by another window
*/
public void setOccluded(boolean isOccluded, boolean animate) {
+ Log.d(TAG, "setOccluded(" + isOccluded + ")");
+
Trace.beginSection("KeyguardViewMediator#setOccluded");
- mLogger.logSetOccluded(isOccluded);
+ if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded);
mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_TRANSITION_FROM_AOD);
mHandler.removeMessages(SET_OCCLUDED);
Message msg = mHandler.obtainMessage(SET_OCCLUDED, isOccluded ? 1 : 0, animate ? 1 : 0);
@@ -1685,7 +1699,7 @@
*/
private void handleSetOccluded(boolean isOccluded, boolean animate) {
Trace.beginSection("KeyguardViewMediator#handleSetOccluded");
- mLogger.logHandleSetOccluded(isOccluded);
+ Log.d(TAG, "handleSetOccluded(" + isOccluded + ")");
synchronized (KeyguardViewMediator.this) {
if (mHiding && isOccluded) {
// We're in the process of going away but WindowManager wants to show a
@@ -1742,7 +1756,7 @@
try {
callback.onInputRestrictedStateChanged(inputRestricted);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnDeviceProvisioned(e);
+ Slog.w(TAG, "Failed to call onDeviceProvisioned", e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(callback);
}
@@ -1757,7 +1771,8 @@
private void doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
if (!mExternallyEnabled) {
- mLogger.logDoKeyguardNotShowingExternallyDisabled();
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
+
mNeedToReshowWhenReenabled = true;
return;
}
@@ -1766,7 +1781,7 @@
// to account for the hiding animation which results in a delay and discrepancy
// between flags
if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) {
- mLogger.logDoKeyguardNotShowingAlreadyShowing();
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
resetStateLocked();
return;
}
@@ -1785,19 +1800,20 @@
|| ((absent || disabled) && requireSim);
if (!lockedOrMissing && shouldWaitForProvisioning()) {
- mLogger.logDoKeyguardNotShowingDeviceNotProvisioned();
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+ + " and the sim is not locked or missing");
return;
}
boolean forceShow = options != null && options.getBoolean(OPTION_FORCE_SHOW, false);
if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
&& !lockedOrMissing && !forceShow) {
- mLogger.logDoKeyguardNotShowingLockScreenOff();
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
return;
}
}
- mLogger.logDoKeyguardShowingLockScreen();
+ if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
showLocked(options);
}
@@ -1835,23 +1851,32 @@
* @see #handleReset
*/
private void resetStateLocked() {
- mLogger.logResetStateLocked();
+ if (DEBUG) Log.e(TAG, "resetStateLocked");
Message msg = mHandler.obtainMessage(RESET);
mHandler.sendMessage(msg);
}
+ /**
+ * Send message to keyguard telling it to verify unlock
+ * @see #handleVerifyUnlock()
+ */
+ private void verifyUnlockLocked() {
+ if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
+ mHandler.sendEmptyMessage(VERIFY_UNLOCK);
+ }
+
private void notifyStartedGoingToSleep() {
- mLogger.logNotifyStartedGoingToSleep();
+ if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep");
mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP);
}
private void notifyFinishedGoingToSleep() {
- mLogger.logNotifyFinishedGoingToSleep();
+ if (DEBUG) Log.d(TAG, "notifyFinishedGoingToSleep");
mHandler.sendEmptyMessage(NOTIFY_FINISHED_GOING_TO_SLEEP);
}
private void notifyStartedWakingUp() {
- mLogger.logNotifyStartedWakingUp();
+ if (DEBUG) Log.d(TAG, "notifyStartedWakingUp");
mHandler.sendEmptyMessage(NOTIFY_STARTED_WAKING_UP);
}
@@ -1861,7 +1886,7 @@
*/
private void showLocked(Bundle options) {
Trace.beginSection("KeyguardViewMediator#showLocked acquiring mShowKeyguardWakeLock");
- mLogger.logShowLocked();
+ if (DEBUG) Log.d(TAG, "showLocked");
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW, options);
@@ -1878,7 +1903,7 @@
*/
private void hideLocked() {
Trace.beginSection("KeyguardViewMediator#hideLocked");
- mLogger.logHideLocked();
+ if (DEBUG) Log.d(TAG, "hideLocked");
Message msg = mHandler.obtainMessage(HIDE);
mHandler.sendMessage(msg);
Trace.endSection();
@@ -1957,7 +1982,8 @@
public void onReceive(Context context, Intent intent) {
if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) {
final int sequence = intent.getIntExtra("seq", 0);
- mLogger.logReceivedDelayedKeyguardAction(sequence, mDelayedShowingSequence);
+ if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
+ + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
synchronized (KeyguardViewMediator.this) {
if (mDelayedShowingSequence == sequence) {
doKeyguardLocked(null);
@@ -1990,7 +2016,7 @@
private void keyguardDone() {
Trace.beginSection("KeyguardViewMediator#keyguardDone");
- mLogger.logKeyguardDone();
+ if (DEBUG) Log.d(TAG, "keyguardDone()");
userActivity();
EventLog.writeEvent(70000, 2);
Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
@@ -2082,7 +2108,7 @@
case KEYGUARD_DONE_PENDING_TIMEOUT:
Trace.beginSection("KeyguardViewMediator#handleMessage"
+ " KEYGUARD_DONE_PENDING_TIMEOUT");
- mLogger.logTimeoutWhileActivityDrawn();
+ Log.w(TAG, "Timeout while waiting for activity drawn!");
Trace.endSection();
break;
case SYSTEM_READY:
@@ -2093,15 +2119,14 @@
};
private void tryKeyguardDone() {
- mLogger.logTryKeyguardDonePending(
- mKeyguardDonePending,
- mHideAnimationRun,
- mHideAnimationRunning
- );
+ if (DEBUG) {
+ Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
+ + mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
+ }
if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
handleKeyguardDone();
} else if (!mHideAnimationRun) {
- mLogger.logTryKeyguardDonePreHideAnimation();
+ if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
mHideAnimationRun = true;
mHideAnimationRunning = true;
mKeyguardViewControllerLazy.get()
@@ -2121,14 +2146,14 @@
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
});
- mLogger.logHandleKeyguardDone();
+ if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
resetKeyguardDonePendingLocked();
}
if (mGoingToSleep) {
mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser);
- mLogger.logDeviceGoingToSleep();
+ Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
return;
}
setPendingLock(false); // user may have authenticated during the screen off animation
@@ -2136,7 +2161,7 @@
try {
mExitSecureCallback.onKeyguardExitResult(true /* authenciated */);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnKeyguardExitResultTrue(e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult()", e);
}
mExitSecureCallback = null;
@@ -2179,9 +2204,9 @@
private void handleKeyguardDoneDrawing() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDoneDrawing");
synchronized(this) {
- mLogger.logHandleKeyguardDoneDrawing();
+ if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing");
if (mWaitingUntilKeyguardVisible) {
- mLogger.logHandleKeyguardDoneDrawingNotifyingKeyguardVisible();
+ if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing: notifying mWaitingUntilKeyguardVisible");
mWaitingUntilKeyguardVisible = false;
notifyAll();
@@ -2231,11 +2256,12 @@
private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
mUiBgExecutor.execute(() -> {
- mLogger.logUpdateActivityLockScreenState(showing, aodShowing);
+ if (DEBUG) {
+ Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
+ }
try {
ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing);
} catch (RemoteException e) {
- mLogger.logFailedToCallSetLockScreenShown(e);
}
});
}
@@ -2252,10 +2278,10 @@
}
synchronized (KeyguardViewMediator.this) {
if (!mSystemReady) {
- mLogger.logIgnoreHandleShow();
+ if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
return;
} else {
- mLogger.logHandleShow();
+ if (DEBUG) Log.d(TAG, "handleShow");
}
mHiding = false;
@@ -2287,7 +2313,7 @@
@Override
public void run() {
Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
- mLogger.logKeyguardGoingAway();
+ if (DEBUG) Log.d(TAG, "keyguardGoingAway");
mKeyguardViewControllerLazy.get().keyguardGoingAway();
int flags = 0;
@@ -2331,7 +2357,7 @@
try {
ActivityTaskManager.getService().keyguardGoingAway(keyguardFlag);
} catch (RemoteException e) {
- mLogger.logFailedToCallKeyguardGoingAway(keyguardFlag, e);
+ Log.e(TAG, "Error while calling WindowManager", e);
}
});
Trace.endSection();
@@ -2339,7 +2365,7 @@
};
private final Runnable mHideAnimationFinishedRunnable = () -> {
- mLogger.logHideAnimationFinishedRunnable();
+ Log.e(TAG, "mHideAnimationFinishedRunnable#run");
mHideAnimationRunning = false;
tryKeyguardDone();
};
@@ -2359,14 +2385,14 @@
}
synchronized (KeyguardViewMediator.this) {
- mLogger.logHandleHide();
+ if (DEBUG) Log.d(TAG, "handleHide");
if (mustNotUnlockCurrentUser()) {
// In split system user mode, we never unlock system user. The end user has to
// switch to another user.
// TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
// still completes and makes the screen blank.
- mLogger.logSplitSystemUserQuitUnlocking();
+ if (DEBUG) Log.d(TAG, "Split system user, quit unlocking.");
mKeyguardExitAnimationRunner = null;
return;
}
@@ -2398,7 +2424,8 @@
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
- mLogger.logHandleStartKeyguardExitAnimation(startTime, fadeoutDuration);
+ Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ + " fadeoutDuration=" + fadeoutDuration);
synchronized (KeyguardViewMediator.this) {
// Tell ActivityManager that we canceled the keyguard animation if
@@ -2414,7 +2441,7 @@
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- mLogger.logFailedToCallOnAnimationFinished(e);
+ Slog.w(TAG, "Failed to call onAnimationFinished", e);
}
}
setShowingLocked(mShowing, true /* force */);
@@ -2437,7 +2464,7 @@
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- mLogger.logFailedToCallOnAnimationFinished(e);
+ Slog.w(TAG, "Failed to call onAnimationFinished", e);
}
onKeyguardExitFinished();
mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
@@ -2456,7 +2483,7 @@
runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps,
wallpapers, nonApps, callback);
} catch (RemoteException e) {
- mLogger.logFailedToCallOnAnimationStart(e);
+ Slog.w(TAG, "Failed to call onAnimationStart", e);
}
// When remaining on the shade, there's no need to do a fancy remote animation,
@@ -2520,7 +2547,7 @@
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- mLogger.logFailedToCallOnAnimationFinished(e);
+ Slog.e(TAG, "RemoteException");
} finally {
mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@@ -2531,7 +2558,7 @@
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- mLogger.logFailedToCallOnAnimationFinished(e);
+ Slog.e(TAG, "RemoteException");
} finally {
mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@@ -2600,9 +2627,11 @@
* @param cancelled {@code true} if the animation was cancelled before it finishes.
*/
public void onKeyguardExitRemoteAnimationFinished(boolean cancelled) {
- mLogger.logOnKeyguardExitRemoteAnimationFinished();
+ Log.d(TAG, "onKeyguardExitRemoteAnimationFinished");
if (!mSurfaceBehindRemoteAnimationRunning && !mSurfaceBehindRemoteAnimationRequested) {
- mLogger.logSkipOnKeyguardExitRemoteAnimationFinished(cancelled, false, false);
+ Log.d(TAG, "skip onKeyguardExitRemoteAnimationFinished cancelled=" + cancelled
+ + " surfaceAnimationRunning=" + mSurfaceBehindRemoteAnimationRunning
+ + " surfaceAnimationRequested=" + mSurfaceBehindRemoteAnimationRequested);
return;
}
@@ -2616,13 +2645,13 @@
onKeyguardExitFinished();
if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
- mLogger.logOnKeyguardExitRemoteAnimationFinishedHideKeyguardView();
+ Log.d(TAG, "onKeyguardExitRemoteAnimationFinished"
+ + "#hideKeyguardViewAfterRemoteAnimation");
mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
} else {
- mLogger.logSkipHideKeyguardViewAfterRemoteAnimation(
- mKeyguardStateController.isDismissingFromSwipe(),
- wasShowing
- );
+ Log.d(TAG, "skip hideKeyguardViewAfterRemoteAnimation"
+ + " dismissFromSwipe=" + mKeyguardStateController.isDismissingFromSwipe()
+ + " wasShowing=" + wasShowing);
}
finishSurfaceBehindRemoteAnimation(cancelled);
@@ -2719,7 +2748,7 @@
}
if (mStatusBarManager == null) {
- mLogger.logCouldNotGetStatusBarManager();
+ Log.w(TAG, "Could not get status bar manager");
} else {
// Disable aspects of the system/status/navigation bars that must not be re-enabled by
// windows that appear on top, ever
@@ -2737,13 +2766,12 @@
}
flags |= StatusBarManager.DISABLE_RECENT;
}
- mLogger.logAdjustStatusBarLocked(
- mShowing,
- mOccluded,
- isSecure(),
- forceHideHomeRecentsButtons,
- Integer.toHexString(flags)
- );
+
+ if (DEBUG) {
+ Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
+ + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+ + " --> flags=0x" + Integer.toHexString(flags));
+ }
mStatusBarManager.disable(flags);
}
@@ -2755,7 +2783,7 @@
*/
private void handleReset() {
synchronized (KeyguardViewMediator.this) {
- mLogger.logHandleReset();
+ if (DEBUG) Log.d(TAG, "handleReset");
mKeyguardViewControllerLazy.get().reset(true /* hideBouncerWhenShowing */);
}
}
@@ -2767,7 +2795,7 @@
private void handleVerifyUnlock() {
Trace.beginSection("KeyguardViewMediator#handleVerifyUnlock");
synchronized (KeyguardViewMediator.this) {
- mLogger.logHandleVerifyUnlock();
+ if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
setShowingLocked(true);
mKeyguardViewControllerLazy.get().dismissAndCollapse();
}
@@ -2776,7 +2804,7 @@
private void handleNotifyStartedGoingToSleep() {
synchronized (KeyguardViewMediator.this) {
- mLogger.logHandleNotifyStartedGoingToSleep();
+ if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
mKeyguardViewControllerLazy.get().onStartedGoingToSleep();
}
}
@@ -2787,7 +2815,7 @@
*/
private void handleNotifyFinishedGoingToSleep() {
synchronized (KeyguardViewMediator.this) {
- mLogger.logHandleNotifyFinishedGoingToSleep();
+ if (DEBUG) Log.d(TAG, "handleNotifyFinishedGoingToSleep");
mKeyguardViewControllerLazy.get().onFinishedGoingToSleep();
}
}
@@ -2795,7 +2823,7 @@
private void handleNotifyStartedWakingUp() {
Trace.beginSection("KeyguardViewMediator#handleMotifyStartedWakingUp");
synchronized (KeyguardViewMediator.this) {
- mLogger.logHandleNotifyWakingUp();
+ if (DEBUG) Log.d(TAG, "handleNotifyWakingUp");
mKeyguardViewControllerLazy.get().onStartedWakingUp();
}
Trace.endSection();
@@ -3061,7 +3089,7 @@
try {
callback.onShowingStateChanged(showing, KeyguardUpdateMonitor.getCurrentUser());
} catch (RemoteException e) {
- mLogger.logFailedToCallOnShowingStateChanged(e);
+ Slog.w(TAG, "Failed to call onShowingStateChanged", e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(callback);
}
@@ -3080,7 +3108,7 @@
try {
mKeyguardStateCallbacks.get(i).onTrustedChanged(trusted);
} catch (RemoteException e) {
- mLogger.logFailedToCallNotifyTrustedChangedLocked(e);
+ Slog.w(TAG, "Failed to call notifyTrustedChangedLocked", e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(i);
}
@@ -3103,7 +3131,7 @@
callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust(
KeyguardUpdateMonitor.getCurrentUser()));
} catch (RemoteException e) {
- mLogger.logFailedToCallIKeyguardStateCallback(e);
+ Slog.w(TAG, "Failed to call to IKeyguardStateCallback", e);
}
}
}
@@ -3180,7 +3208,7 @@
// internal state to reflect that immediately, vs. waiting for the launch animator to
// begin. Otherwise, calls to setShowingLocked, etc. will not know that we're about to
// be occluded and might re-show the keyguard.
- mLogger.logOccludeAnimatorOnAnimationStart();
+ Log.d(TAG, "OccludeAnimator#onAnimationStart. Set occluded = true.");
setOccluded(true /* isOccluded */, false /* animate */);
}
@@ -3188,7 +3216,8 @@
public void onAnimationCancelled(boolean isKeyguardOccluded) throws RemoteException {
super.onAnimationCancelled(isKeyguardOccluded);
- mLogger.logOccludeAnimationCancelledByWm(isKeyguardOccluded);
+ Log.d(TAG, "Occlude animation cancelled by WM. "
+ + "Setting occluded state to: " + isKeyguardOccluded);
setOccluded(isKeyguardOccluded /* occluded */, false /* animate */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index fdea62d..56f1ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -30,7 +30,6 @@
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
-import com.android.keyguard.logging.KeyguardViewMediatorLogger;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -106,8 +105,7 @@
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
- KeyguardViewMediatorLogger logger) {
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
return new KeyguardViewMediator(
context,
falsingCollector,
@@ -134,8 +132,7 @@
interactionJankMonitor,
dreamOverlayStateController,
notificationShadeWindowController,
- activityLaunchAnimator,
- logger);
+ activityLaunchAnimator);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 77ad806..6124e10 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -158,13 +158,8 @@
* add more detail to every log may do more to improve overall logging than adding more logs
* with this method.
*/
- fun log(
- tag: String,
- level: LogLevel,
- @CompileTimeConstant message: String,
- exception: Throwable? = null
- ) =
- log(tag, level, {str1 = message}, { str1!! }, exception)
+ fun log(tag: String, level: LogLevel, @CompileTimeConstant message: String) =
+ log(tag, level, {str1 = message}, { str1!! })
/**
* You should call [log] instead of this method.
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
index 684839f..323ee21 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
@@ -1,7 +1,4 @@
package com.android.systemui.log.dagger
-import javax.inject.Qualifier
-
/** A [com.android.systemui.log.LogBuffer] for KeyguardUpdateMonitor. */
-@Qualifier
annotation class KeyguardUpdateMonitorLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 9af42f8..c2a8764 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -305,15 +305,4 @@
public static LogBuffer provideKeyguardUpdateMonitorLogBuffer(LogBufferFactory factory) {
return factory.create("KeyguardUpdateMonitorLog", 200);
}
-
- /**
- * Provides a {@link LogBuffer} for use by
- * {@link com.android.systemui.keyguard.KeyguardViewMediator}.
- */
- @Provides
- @SysUISingleton
- @KeyguardViewMediatorLog
- public static LogBuffer provideKeyguardViewMediatorLogBuffer(LogBufferFactory factory) {
- return factory.create("KeyguardViewMediatorLog", 100);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 012d766..b02393b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -488,8 +488,8 @@
TextView deviceName = mMediaViewHolder.getSeamlessText();
final MediaDeviceData device = data.getDevice();
- final boolean enabled;
- final boolean seamlessDisabled;
+ final boolean isTapEnabled;
+ final boolean useDisabledAlpha;
final int iconResource;
CharSequence deviceString;
if (showBroadcastButton) {
@@ -499,21 +499,25 @@
&& TextUtils.equals(device.getName(),
MediaDataUtils.getAppLabel(mContext, mPackageName, mContext.getString(
R.string.bt_le_audio_broadcast_dialog_unknown_name)));
- seamlessDisabled = !mIsCurrentBroadcastedApp;
+ useDisabledAlpha = !mIsCurrentBroadcastedApp;
// Always be enabled if the broadcast button is shown
- enabled = true;
+ isTapEnabled = true;
+
+ // Defaults for broadcasting state
deviceString = mContext.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name);
iconResource = R.drawable.settings_input_antenna;
} else {
// Disable clicking on output switcher for invalid devices and resumption controls
- seamlessDisabled = (device != null && !device.getEnabled()) || data.getResumption();
- enabled = !seamlessDisabled;
+ useDisabledAlpha = (device != null && !device.getEnabled()) || data.getResumption();
+ isTapEnabled = !useDisabledAlpha;
+
+ // Defaults for non-broadcasting state
deviceString = mContext.getString(R.string.media_seamless_other_device);
iconResource = R.drawable.ic_media_home_devices;
}
- mMediaViewHolder.getSeamlessButton().setAlpha(seamlessDisabled ? DISABLED_ALPHA : 1.0f);
- seamlessView.setEnabled(enabled);
+ mMediaViewHolder.getSeamlessButton().setAlpha(useDisabledAlpha ? DISABLED_ALPHA : 1.0f);
+ seamlessView.setEnabled(isTapEnabled);
if (device != null) {
Drawable icon = device.getIcon();
@@ -524,7 +528,9 @@
} else {
iconView.setImageDrawable(icon);
}
- deviceString = device.getName();
+ if (device.getName() != null) {
+ deviceString = device.getName();
+ }
} else {
// Set to default icon
iconView.setImageResource(iconResource);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 7b497ad..c48271e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -518,9 +518,20 @@
}
val actions = createActionsFromState(it.packageName,
mediaControllerFactory.create(it.token), UserHandle(it.userId))
- val data = it.copy(
- semanticActions = actions,
- isPlaying = isPlayingState(state.state))
+
+ // Control buttons
+ // If flag is enabled and controller has a PlaybackState,
+ // create actions from session info
+ // otherwise, no need to update semantic actions.
+ val data = if (actions != null) {
+ it.copy(
+ semanticActions = actions,
+ isPlaying = isPlayingState(state.state))
+ } else {
+ it.copy(
+ isPlaying = isPlayingState(state.state)
+ )
+ }
if (DEBUG) Log.d(TAG, "State updated outside of notification")
onMediaDataLoaded(key, key, data)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 2518659..b3a4ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -348,7 +348,11 @@
// If we have a controller but get a null route, then don't trust the device
val enabled = device != null && (controller == null || route != null)
- val name = route?.name?.toString() ?: device?.name
+ val name = if (controller == null || route != null) {
+ route?.name?.toString() ?: device?.name
+ } else {
+ null
+ }
current = MediaDeviceData(enabled, device?.iconWithoutBackground, name,
id = device?.id, showBroadcastButton = false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 08cf57c..b39770d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -356,15 +356,6 @@
mHeaderSubtitle.setText(subTitle);
mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
}
- if (!mAdapter.isDragging()) {
- int currentActivePosition = mAdapter.getCurrentActivePosition();
- if (!colorSetUpdated && !deviceSetChanged && currentActivePosition >= 0
- && currentActivePosition < mAdapter.getItemCount()) {
- mAdapter.notifyItemChanged(currentActivePosition);
- } else {
- mAdapter.notifyDataSetChanged();
- }
- }
// Show when remote media session is available or
// when the device supports BT LE audio + media is playing
mStopButton.setVisibility(getStopButtonVisibility());
@@ -374,6 +365,18 @@
mBroadcastIcon.setVisibility(getBroadcastIconVisibility());
mBroadcastIcon.setOnClickListener(v -> onBroadcastIconClick());
+ if (!mAdapter.isDragging()) {
+ int currentActivePosition = mAdapter.getCurrentActivePosition();
+ if (!colorSetUpdated && !deviceSetChanged && currentActivePosition >= 0
+ && currentActivePosition < mAdapter.getItemCount()) {
+ mAdapter.notifyItemChanged(currentActivePosition);
+ } else {
+ mAdapter.notifyDataSetChanged();
+ }
+ } else {
+ mMediaOutputController.setRefreshing(false);
+ mMediaOutputController.refreshDataSetIfNeeded();
+ }
}
private void updateButtonBackgroundColorFilter() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index c544871..acd04f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -25,6 +25,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.DreamMediaEntryComplication;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaData;
import com.android.systemui.media.MediaDataManager;
@@ -54,7 +55,7 @@
}
mAdded = false;
- mDreamOverlayStateController.removeComplication(mComplication);
+ mDreamOverlayStateController.removeComplication(mMediaEntryComplication);
}
@Override
@@ -79,24 +80,24 @@
}
mAdded = true;
- mDreamOverlayStateController.addComplication(mComplication);
+ mDreamOverlayStateController.addComplication(mMediaEntryComplication);
}
};
private final MediaDataManager mMediaDataManager;
private final DreamOverlayStateController mDreamOverlayStateController;
- private final MediaDreamComplication mComplication;
+ private final DreamMediaEntryComplication mMediaEntryComplication;
private final FeatureFlags mFeatureFlags;
@Inject
public MediaDreamSentinel(Context context, MediaDataManager mediaDataManager,
DreamOverlayStateController dreamOverlayStateController,
- MediaDreamComplication complication,
+ DreamMediaEntryComplication mediaEntryComplication,
FeatureFlags featureFlags) {
super(context);
mMediaDataManager = mediaDataManager;
mDreamOverlayStateController = dreamOverlayStateController;
- mComplication = complication;
+ mMediaEntryComplication = mediaEntryComplication;
mFeatureFlags = featureFlags;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java
index 3408d97..052608f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java
@@ -16,6 +16,8 @@
package com.android.systemui.media.dream.dagger;
+import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_MEDIA_COMPLICATION_WEIGHT;
+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import android.content.Context;
@@ -93,7 +95,7 @@
ComplicationLayoutParams.POSITION_TOP
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_DOWN,
- 0,
+ DREAM_MEDIA_COMPLICATION_WEIGHT,
true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 00a22f2..35a6c74 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -30,7 +30,6 @@
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
-import androidx.core.graphics.ColorUtils
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
@@ -209,8 +208,7 @@
// Center the ripple on the bottom of the screen in the middle.
rippleView.setCenter(width * 0.5f, height.toFloat())
val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
- val colorWithAlpha = ColorUtils.setAlphaComponent(color, 70)
- rippleView.setColor(colorWithAlpha)
+ rippleView.setColor(color, 70)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index a153cb6..f93c671 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -26,6 +26,7 @@
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
+import com.android.systemui.plugins.FalsingManager
/**
* A class enumerating all the possible states of the media tap-to-transfer chip on the sender
@@ -106,12 +107,15 @@
controllerSender: MediaTttChipControllerSender,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
- uiEventLogger: MediaTttSenderUiEventLogger
+ uiEventLogger: MediaTttSenderUiEventLogger,
+ falsingManager: FalsingManager,
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
+
uiEventLogger.logUndoClicked(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED
)
@@ -141,12 +145,15 @@
controllerSender: MediaTttChipControllerSender,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
- uiEventLogger: MediaTttSenderUiEventLogger
+ uiEventLogger: MediaTttSenderUiEventLogger,
+ falsingManager: FalsingManager,
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
+
uiEventLogger.logUndoClicked(
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
)
@@ -212,7 +219,8 @@
controllerSender: MediaTttChipControllerSender,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
- uiEventLogger: MediaTttSenderUiEventLogger
+ uiEventLogger: MediaTttSenderUiEventLogger,
+ falsingManager: FalsingManager,
): View.OnClickListener? = null
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 9335489..5ad82fd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -22,21 +22,25 @@
import android.os.PowerManager
import android.util.Log
import android.view.Gravity
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.widget.TextView
import com.android.internal.statusbar.IUndoMediaTransferCallback
+import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ViewHierarchyAnimator
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -58,7 +62,9 @@
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
- private val uiEventLogger: MediaTttSenderUiEventLogger
+ private val uiEventLogger: MediaTttSenderUiEventLogger,
+ private val falsingManager: FalsingManager,
+ private val falsingCollector: FalsingCollector,
) : MediaTttChipControllerCommon<ChipSenderInfo>(
context,
logger,
@@ -70,6 +76,9 @@
powerManager,
R.layout.media_ttt_chip,
) {
+
+ private lateinit var parent: MediaTttChipRootView
+
override val windowLayoutParams = commonWindowLayoutParams.apply {
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
}
@@ -121,6 +130,15 @@
val chipState = newChipInfo.state
+ // Detect falsing touches on the chip.
+ parent = currentChipView as MediaTttChipRootView
+ parent.touchHandler = object : Gefingerpoken {
+ override fun onTouchEvent(ev: MotionEvent?): Boolean {
+ falsingCollector.onTouchEvent(ev)
+ return false
+ }
+ }
+
// App icon
val iconName = setIcon(currentChipView, newChipInfo.routeInfo.clientPackageName)
@@ -136,7 +154,11 @@
// Undo
val undoView = currentChipView.requireViewById<View>(R.id.undo)
val undoClickListener = chipState.undoClickListener(
- this, newChipInfo.routeInfo, newChipInfo.undoCallback, uiEventLogger
+ this,
+ newChipInfo.routeInfo,
+ newChipInfo.undoCallback,
+ uiEventLogger,
+ falsingManager,
)
undoView.setOnClickListener(undoClickListener)
undoView.visibility = (undoClickListener != null).visibleIfTrue()
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipRootView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipRootView.kt
new file mode 100644
index 0000000..3373159
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipRootView.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.systemui.media.taptotransfer.sender
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.widget.FrameLayout
+import com.android.systemui.Gefingerpoken
+
+/** A simple subclass that allows for observing touch events on chip. */
+class MediaTttChipRootView(
+ context: Context,
+ attrs: AttributeSet?
+) : FrameLayout(context, attrs) {
+
+ /** Assign this field to observe touch events. */
+ var touchHandler: Gefingerpoken? = null
+
+ override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
+ touchHandler?.onTouchEvent(ev)
+ return super.dispatchTouchEvent(ev)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
index 3f93108..5da4809 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
@@ -17,7 +17,14 @@
import javax.inject.Inject;
-/** */
+/**
+ * Plays a animation to reveal newly added QS tiles.
+ *
+ * The aniumation is played when the user fully opens Quick Settings, and is only shown for
+ * <li> tiles added automatically (not through user customization)
+ * <li> tiles not have been revealed before (memoized via {@code QS_TILE_SPECS_REVEALED}
+ * preference)
+ */
public class QSTileRevealController {
private static final long QS_REVEAL_TILES_DELAY = 500L;
@@ -39,6 +46,7 @@
});
}
};
+
QSTileRevealController(Context context, QSPanelController qsPanelController,
PagedTileLayout pagedTileLayout, QSCustomizerController qsCustomizerController) {
mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
index db7c1fd..d2f3a6a 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
@@ -68,7 +68,7 @@
float rippleInsideAlpha = (1.-inside) * in_fadeFill;
float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a;
- vec4 ripple = in_color * rippleAlpha;
+ vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha;
return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
}
"""
@@ -84,7 +84,7 @@
float rippleInsideAlpha = (1.-inside) * in_fadeFill;
float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a;
- vec4 ripple = in_color * rippleAlpha;
+ vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha;
return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
}
"""
@@ -100,7 +100,7 @@
float rippleInsideAlpha = (1.-inside) * in_fadeFill;
float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a;
- vec4 ripple = in_color * rippleAlpha;
+ vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha;
return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
}
"""
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index 60c8f37..1e51ffa 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -25,10 +25,12 @@
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
+import androidx.core.graphics.ColorUtils
import com.android.systemui.ripple.RippleShader.RippleShape
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
private const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
+const val RIPPLE_DEFAULT_ALPHA: Int = 45
/**
* A generic expanding ripple effect.
@@ -111,9 +113,12 @@
rippleInProgress = true
}
- /** Set the color to be used for the ripple. */
- fun setColor(color: Int) {
- rippleShader.color = color
+ /** Set the color to be used for the ripple.
+ *
+ * The alpha value of the color will be applied to the ripple. The alpha range is [0-100].
+ */
+ fun setColor(color: Int, alpha: Int = RIPPLE_DEFAULT_ALPHA) {
+ rippleShader.color = ColorUtils.setAlphaComponent(color, alpha)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index a918e5d..309059f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot
import android.graphics.Insets
+import android.util.Log
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
@@ -61,8 +62,9 @@
) {
val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
+ Log.d(TAG, "findPrimaryContent: $info")
- result = if (policy.isManagedProfile(info.userId)) {
+ result = if (policy.isManagedProfile(info.user.identifier)) {
val image = capture.captureTask(info.taskId)
?: error("Task snapshot returned a null Bitmap!")
@@ -70,7 +72,7 @@
ScreenshotRequest(
TAKE_SCREENSHOT_PROVIDED_IMAGE, request.source,
HardwareBitmapBundler.hardwareBitmapToBundle(image),
- info.bounds, Insets.NONE, info.taskId, info.userId, info.component
+ info.bounds, Insets.NONE, info.taskId, info.user.identifier, info.component
)
} else {
// Create a new request of the same type which includes the top component
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt
index 3580010..f73d204 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt
@@ -19,6 +19,7 @@
import android.annotation.UserIdInt
import android.content.ComponentName
import android.graphics.Rect
+import android.os.UserHandle
import android.view.Display
/**
@@ -42,7 +43,7 @@
data class DisplayContentInfo(
val component: ComponentName,
val bounds: Rect,
- @UserIdInt val userId: Int,
+ val user: UserHandle,
val taskId: Int,
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
index ba809f6..c2a5060 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
@@ -29,9 +29,11 @@
import android.graphics.Rect
import android.os.Process
import android.os.RemoteException
+import android.os.UserHandle
import android.os.UserManager
import android.util.Log
import android.view.Display.DEFAULT_DISPLAY
+import com.android.internal.annotations.VisibleForTesting
import com.android.internal.infra.ServiceConnector
import com.android.systemui.SystemUIService
import com.android.systemui.dagger.SysUISingleton
@@ -45,21 +47,13 @@
import kotlinx.coroutines.withContext
@SysUISingleton
-internal class ScreenshotPolicyImpl @Inject constructor(
+internal open class ScreenshotPolicyImpl @Inject constructor(
context: Context,
private val userMgr: UserManager,
private val atmService: IActivityTaskManager,
@Background val bgDispatcher: CoroutineDispatcher,
) : ScreenshotPolicy {
- private val systemUiContent =
- DisplayContentInfo(
- ComponentName(context, SystemUIService::class.java),
- Rect(),
- ActivityTaskManager.INVALID_TASK_ID,
- Process.myUserHandle().identifier,
- )
-
private val proxyConnector: ServiceConnector<IScreenshotProxy> =
ServiceConnector.Impl(
context,
@@ -78,6 +72,9 @@
}
private fun nonPipVisibleTask(info: RootTaskInfo): Boolean {
+ if (DEBUG) {
+ debugLogRootTaskInfo(info)
+ }
return info.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED &&
info.isVisible &&
info.isRunning &&
@@ -99,58 +96,46 @@
}
val taskInfoList = getAllRootTaskInfosOnDisplay(displayId)
- if (DEBUG) {
- debugLogRootTaskInfos(taskInfoList)
- }
// If no visible task is located, then report SystemUI as the foreground content
val target = taskInfoList.firstOrNull(::nonPipVisibleTask) ?: return systemUiContent
-
- val topActivity: ComponentName = target.topActivity ?: error("should not be null")
- val topChildTask = target.childTaskIds.size - 1
- val childTaskId = target.childTaskIds[topChildTask]
- val childTaskUserId = target.childTaskUserIds[topChildTask]
- val childTaskBounds = target.childTaskBounds[topChildTask]
-
- return DisplayContentInfo(topActivity, childTaskBounds, childTaskId, childTaskUserId)
+ return target.toDisplayContentInfo()
}
- private fun debugLogRootTaskInfos(taskInfoList: List<RootTaskInfo>) {
- for (info in taskInfoList) {
- Log.d(
- TAG,
- "[root task info] " +
- "taskId=${info.taskId} " +
- "parentTaskId=${info.parentTaskId} " +
- "position=${info.position} " +
- "positionInParent=${info.positionInParent} " +
- "isVisible=${info.isVisible()} " +
- "visible=${info.visible} " +
- "isFocused=${info.isFocused} " +
- "isSleeping=${info.isSleeping} " +
- "isRunning=${info.isRunning} " +
- "windowMode=${windowingModeToString(info.windowingMode)} " +
- "activityType=${activityTypeToString(info.activityType)} " +
- "topActivity=${info.topActivity} " +
- "topActivityInfo=${info.topActivityInfo} " +
- "numActivities=${info.numActivities} " +
- "childTaskIds=${Arrays.toString(info.childTaskIds)} " +
- "childUserIds=${Arrays.toString(info.childTaskUserIds)} " +
- "childTaskBounds=${Arrays.toString(info.childTaskBounds)} " +
- "childTaskNames=${Arrays.toString(info.childTaskNames)}"
- )
+ private fun debugLogRootTaskInfo(info: RootTaskInfo) {
+ Log.d(TAG, "RootTaskInfo={" +
+ "taskId=${info.taskId} " +
+ "parentTaskId=${info.parentTaskId} " +
+ "position=${info.position} " +
+ "positionInParent=${info.positionInParent} " +
+ "isVisible=${info.isVisible()} " +
+ "visible=${info.visible} " +
+ "isFocused=${info.isFocused} " +
+ "isSleeping=${info.isSleeping} " +
+ "isRunning=${info.isRunning} " +
+ "windowMode=${windowingModeToString(info.windowingMode)} " +
+ "activityType=${activityTypeToString(info.activityType)} " +
+ "topActivity=${info.topActivity} " +
+ "topActivityInfo=${info.topActivityInfo} " +
+ "numActivities=${info.numActivities} " +
+ "childTaskIds=${Arrays.toString(info.childTaskIds)} " +
+ "childUserIds=${Arrays.toString(info.childTaskUserIds)} " +
+ "childTaskBounds=${Arrays.toString(info.childTaskBounds)} " +
+ "childTaskNames=${Arrays.toString(info.childTaskNames)}" +
+ "}"
+ )
- for (j in 0 until info.childTaskIds.size) {
- Log.d(TAG, " *** [$j] ******")
- Log.d(TAG, " *** childTaskIds[$j]: ${info.childTaskIds[j]}")
- Log.d(TAG, " *** childTaskUserIds[$j]: ${info.childTaskUserIds[j]}")
- Log.d(TAG, " *** childTaskBounds[$j]: ${info.childTaskBounds[j]}")
- Log.d(TAG, " *** childTaskNames[$j]: ${info.childTaskNames[j]}")
- }
+ for (j in 0 until info.childTaskIds.size) {
+ Log.d(TAG, " *** [$j] ******")
+ Log.d(TAG, " *** childTaskIds[$j]: ${info.childTaskIds[j]}")
+ Log.d(TAG, " *** childTaskUserIds[$j]: ${info.childTaskUserIds[j]}")
+ Log.d(TAG, " *** childTaskBounds[$j]: ${info.childTaskBounds[j]}")
+ Log.d(TAG, " *** childTaskNames[$j]: ${info.childTaskNames[j]}")
}
}
- private suspend fun getAllRootTaskInfosOnDisplay(displayId: Int): List<RootTaskInfo> =
+ @VisibleForTesting
+ open suspend fun getAllRootTaskInfosOnDisplay(displayId: Int): List<RootTaskInfo> =
withContext(bgDispatcher) {
try {
atmService.getAllRootTaskInfosOnDisplay(displayId)
@@ -160,7 +145,8 @@
}
}
- private suspend fun isNotificationShadeExpanded(): Boolean = suspendCoroutine { k ->
+ @VisibleForTesting
+ open suspend fun isNotificationShadeExpanded(): Boolean = suspendCoroutine { k ->
proxyConnector
.postForResult { it.isNotificationShadeExpanded }
.whenComplete { expanded, error ->
@@ -171,8 +157,30 @@
}
}
- companion object {
- const val TAG: String = "ScreenshotPolicyImpl"
- const val DEBUG: Boolean = false
- }
+ @VisibleForTesting
+ internal val systemUiContent =
+ DisplayContentInfo(
+ ComponentName(context, SystemUIService::class.java),
+ Rect(),
+ Process.myUserHandle(),
+ ActivityTaskManager.INVALID_TASK_ID
+ )
+}
+
+private const val TAG: String = "ScreenshotPolicyImpl"
+private const val DEBUG: Boolean = false
+
+@VisibleForTesting
+internal fun RootTaskInfo.toDisplayContentInfo(): DisplayContentInfo {
+ val topActivity: ComponentName = topActivity ?: error("should not be null")
+ val topChildTask = childTaskIds.size - 1
+ val childTaskId = childTaskIds[topChildTask]
+ val childTaskUserId = childTaskUserIds[topChildTask]
+ val childTaskBounds = childTaskBounds[topChildTask]
+
+ return DisplayContentInfo(
+ topActivity,
+ childTaskBounds,
+ UserHandle.of(childTaskUserId),
+ childTaskId)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 35f32ca..695a80b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -229,6 +229,7 @@
Log.d(TAG, "handleMessage: Using request processor");
mProcessor.processAsync(request,
(r) -> dispatchToController(r, onSaved, callback));
+ return;
}
dispatchToController(request, onSaved, callback);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java
index 9818af3..1cdacb9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java
@@ -17,32 +17,30 @@
package com.android.systemui.shade;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.TapAgainView;
-public class NotificationPanelView extends PanelView {
+/** The shade view. */
+public final class NotificationPanelView extends FrameLayout {
+ static final boolean DEBUG = false;
- private static final boolean DEBUG = false;
-
- /**
- * Fling expanding QS.
- */
- public static final int FLING_EXPAND = 0;
-
- public static final String COUNTER_PANEL_OPEN = "panel_open";
- public static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
+ private final Paint mAlphaPaint = new Paint();
private int mCurrentPanelAlpha;
- private final Paint mAlphaPaint = new Paint();
private boolean mDozing;
private RtlChangeListener mRtlChangeListener;
+ private NotificationPanelViewController.TouchHandler mTouchHandler;
+ private OnConfigurationChangedListener mOnConfigurationChangedListener;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -99,7 +97,36 @@
return findViewById(R.id.shade_falsing_tap_again);
}
+ /** Sets the touch handler for this view. */
+ public void setOnTouchListener(NotificationPanelViewController.TouchHandler touchHandler) {
+ super.setOnTouchListener(touchHandler);
+ mTouchHandler = touchHandler;
+ }
+
+ void setOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
+ mOnConfigurationChangedListener = listener;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return mTouchHandler.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public void dispatchConfigurationChanged(Configuration newConfig) {
+ super.dispatchConfigurationChanged(newConfig);
+ mOnConfigurationChangedListener.onConfigurationChanged(newConfig);
+ }
+
+ /** Callback for right-to-left setting changes. */
interface RtlChangeListener {
+ /** Called when right-to-left setting changes. */
void onRtlPropertielsChanged(int layoutDirection);
}
+
+ /** Callback for config changes. */
+ interface OnConfigurationChangedListener {
+ /** Called when configuration changes. */
+ void onConfigurationChanged(Configuration newConfig);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 5f07f9f..9a8395c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -33,7 +33,7 @@
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
-import static com.android.systemui.shade.PanelView.DEBUG;
+import static com.android.systemui.shade.NotificationPanelView.DEBUG;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -248,7 +248,7 @@
@CentralSurfacesComponent.CentralSurfacesScope
public final class NotificationPanelViewController {
- public static final String TAG = PanelView.class.getSimpleName();
+ public static final String TAG = "PanelView";
public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
public static final float FLING_SPEED_UP_FACTOR = 0.6f;
public static final float FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f;
@@ -274,7 +274,7 @@
/**
* Fling expanding QS.
*/
- private static final int FLING_EXPAND = 0;
+ public static final int FLING_EXPAND = 0;
/**
* Fling collapsing QS, potentially stopping when QS becomes QQS.
@@ -430,7 +430,8 @@
private boolean mQsTracking;
/**
- * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
+ * If set, the ongoing touch gesture might both trigger the expansion in {@link
+ * NotificationPanelView} and
* the expansion for quick settings.
*/
private boolean mConflictingQsExpansionGesture;
@@ -5405,13 +5406,13 @@
return animator;
}
- /** Update the visibility of {@link PanelView} if necessary. */
+ /** Update the visibility of {@link NotificationPanelView} if necessary. */
public void updateVisibility() {
mView.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
}
/**
- * Updates the panel expansion and {@link PanelView} visibility if necessary.
+ * Updates the panel expansion and {@link NotificationPanelView} visibility if necessary.
*
* TODO(b/200063118): Could public calls to this method be replaced with calls to
* {@link #updateVisibility()}? That would allow us to make this method private.
@@ -6170,7 +6171,7 @@
}
public class OnConfigurationChangedListener implements
- PanelView.OnConfigurationChangedListener {
+ NotificationPanelView.OnConfigurationChangedListener {
@Override
public void onConfigurationChanged(Configuration newConfig) {
loadDimens();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java b/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
deleted file mode 100644
index 4349d81..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012 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.systemui.shade;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
-
-public abstract class PanelView extends FrameLayout {
- public static final boolean DEBUG = false;
- public static final String TAG = PanelView.class.getSimpleName();
- private NotificationPanelViewController.TouchHandler mTouchHandler;
-
- protected CentralSurfaces mCentralSurfaces;
- protected HeadsUpManagerPhone mHeadsUpManager;
-
- protected KeyguardBottomAreaView mKeyguardBottomArea;
- private OnConfigurationChangedListener mOnConfigurationChangedListener;
-
- public PanelView(Context context) {
- super(context);
- }
-
- public PanelView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public PanelView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public void setOnTouchListener(NotificationPanelViewController.TouchHandler touchHandler) {
- super.setOnTouchListener(touchHandler);
- mTouchHandler = touchHandler;
- }
-
- public void setOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
- mOnConfigurationChangedListener = listener;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mTouchHandler.onInterceptTouchEvent(event);
- }
-
- @Override
- public void dispatchConfigurationChanged(Configuration newConfig) {
- super.dispatchConfigurationChanged(newConfig);
- mOnConfigurationChangedListener.onConfigurationChanged(newConfig);
- }
-
- interface OnConfigurationChangedListener {
- void onConfigurationChanged(Configuration newConfig);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index b9684fc..3d161d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -24,6 +24,8 @@
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -73,6 +75,7 @@
}
@Override
+ @NonNull
public ExpandableViewState createExpandableViewState() {
return new EmptyShadeViewState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt
index 2ca1beb..7b49ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt
@@ -1,7 +1,6 @@
package com.android.systemui.statusbar
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import javax.inject.Inject
@@ -12,14 +11,12 @@
*/
@SysUISingleton
class NotificationInteractionTracker @Inject constructor(
- private val clicker: NotificationClickNotifier,
- private val entryManager: NotificationEntryManager
+ clicker: NotificationClickNotifier,
) : NotifCollectionListener, NotificationInteractionListener {
private val interactions = mutableMapOf<String, Boolean>()
init {
clicker.addNotificationInteractionListener(this)
- entryManager.addCollectionListener(this)
}
fun hasUserInteractedWith(key: String): Boolean {
@@ -38,5 +35,3 @@
interactions[key] = true
}
}
-
-private const val TAG = "NotificationInteractionTracker"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index d1bc14f..d908243 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -50,12 +50,6 @@
void addUserChangedListener(UserChangedListener listener);
/**
- * Registers a [KeyguardNotificationSuppressor] that will be consulted during
- * {@link #shouldShowOnKeyguard(NotificationEntry)}
- */
- void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor);
-
- /**
* Removes a listener previously registered with
* {@link #addUserChangedListener(UserChangedListener)}
*/
@@ -63,14 +57,8 @@
SparseArray<UserInfo> getCurrentProfiles();
- void setLockscreenPublicMode(boolean isProfilePublic, int userId);
-
boolean shouldShowLockscreenNotifications();
- boolean shouldHideNotifications(int userId);
- boolean shouldHideNotifications(String key);
- boolean shouldShowOnKeyguard(NotificationEntry entry);
-
boolean isAnyProfilePublicMode();
void updatePublicMode();
@@ -108,11 +96,6 @@
default void onUserRemoved(int userId) {}
}
- /** Used to hide notifications on the lockscreen */
- interface KeyguardNotificationSuppressor {
- boolean shouldSuppressOnKeyguard(NotificationEntry entry);
- }
-
/**
* Notified when any state pertaining to Notifications has changed; any methods pertaining to
* notifications should be re-queried.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f4ca7ed..ae5a2c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,17 +15,13 @@
*/
package com.android.systemui.statusbar;
-import static android.app.Notification.VISIBILITY_SECRET;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
-import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -42,9 +38,10 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -110,7 +107,6 @@
private LockPatternUtils mLockPatternUtils;
protected KeyguardManager mKeyguardManager;
private int mState = StatusBarState.SHADE;
- private List<KeyguardNotificationSuppressor> mKeyguardSuppressors = new ArrayList<>();
private final ListenerSet<NotificationStateChangedListener> mNotifStateChangedListeners =
new ListenerSet<>();
@@ -344,67 +340,6 @@
}
}
- /**
- * Returns true if notifications are temporarily disabled for this user for security reasons,
- * regardless of the normal settings for that user.
- */
- private boolean shouldTemporarilyHideNotifications(int userId) {
- if (userId == UserHandle.USER_ALL) {
- userId = mCurrentUserId;
- }
- boolean inLockdown = Dependency.get(KeyguardUpdateMonitor.class).isUserInLockdown(userId);
- mUsersInLockdownLatestResult.put(userId, inLockdown);
- return inLockdown;
- }
-
- /**
- * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
- * If so, notifications should be hidden.
- */
- public boolean shouldHideNotifications(int userId) {
- boolean hide = isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
- || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId))
- || shouldTemporarilyHideNotifications(userId);
- mShouldHideNotifsLatestResult.put(userId, hide);
- return hide;
- }
-
- /**
- * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
- * package-specific override.
- */
- public boolean shouldHideNotifications(String key) {
- if (mCommonNotifCollectionLazy.get() == null) {
- Log.wtf(TAG, "mCommonNotifCollectionLazy was null!", new Throwable());
- return true;
- }
- NotificationEntry visibleEntry = mCommonNotifCollectionLazy.get().getEntry(key);
- return isLockscreenPublicMode(mCurrentUserId) && visibleEntry != null
- && visibleEntry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET;
- }
-
- public boolean shouldShowOnKeyguard(NotificationEntry entry) {
- if (mCommonNotifCollectionLazy.get() == null) {
- Log.wtf(TAG, "mCommonNotifCollectionLazy was null!", new Throwable());
- return false;
- }
- for (int i = 0; i < mKeyguardSuppressors.size(); i++) {
- if (mKeyguardSuppressors.get(i).shouldSuppressOnKeyguard(entry)) {
- return false;
- }
- }
- boolean exceedsPriorityThreshold;
- if (mHideSilentNotificationsOnLockscreen) {
- exceedsPriorityThreshold =
- entry.getBucket() == BUCKET_MEDIA_CONTROLS
- || (entry.getBucket() != BUCKET_SILENT
- && entry.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT);
- } else {
- exceedsPriorityThreshold = !entry.getRanking().isAmbient();
- }
- return mShowLockscreenNotifications && exceedsPriorityThreshold;
- }
-
private void setShowLockscreenNotifications(boolean show) {
mShowLockscreenNotifications = show;
}
@@ -491,7 +426,8 @@
/**
* Save the current "public" (locked and secure) state of the lockscreen.
*/
- public void setLockscreenPublicMode(boolean publicMode, int userId) {
+ @VisibleForTesting
+ void setLockscreenPublicMode(boolean publicMode, int userId) {
mLockscreenPublicMode.put(userId, publicMode);
}
@@ -674,11 +610,6 @@
}
@Override
- public void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor) {
- mKeyguardSuppressors.add(suppressor);
- }
-
- @Override
public void removeUserChangedListener(UserChangedListener listener) {
mListeners.remove(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 2214287..cea3deb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -29,6 +29,8 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
@@ -153,6 +155,7 @@
}
@Override
+ @NonNull
public ExpandableViewState createExpandableViewState() {
return new ShelfState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 8cb18a0..59022c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -50,7 +50,6 @@
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -76,20 +75,22 @@
private final Executor mUiBgExecutor;
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
private final CommandQueue mCommandQueue;
- private KeyguardStateController mKeyguardStateController;
+ private final KeyguardStateController mKeyguardStateController;
@Inject
- public InstantAppNotifier(Context context, CommandQueue commandQueue,
- @UiBackground Executor uiBgExecutor) {
+ public InstantAppNotifier(
+ Context context,
+ CommandQueue commandQueue,
+ @UiBackground Executor uiBgExecutor,
+ KeyguardStateController keyguardStateController) {
super(context);
mCommandQueue = commandQueue;
mUiBgExecutor = uiBgExecutor;
+ mKeyguardStateController = keyguardStateController;
}
@Override
public void start() {
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
-
// listen for user / profile change.
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index e2f87b6..54be9a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -23,7 +23,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
@@ -47,7 +46,7 @@
* user. After an entry makes its way into the active state, we sort and filter the entire set to
* repopulate the visible set.
*/
-public class NotificationEntryManager implements VisualStabilityManager.Callback {
+public class NotificationEntryManager {
private final NotificationEntryManagerLogger mLogger;
@@ -85,11 +84,6 @@
mNotificationEntryListeners.remove(listener);
}
- @Override
- public void onChangeAllowed() {
- updateNotifications("reordering is now allowed");
- }
-
/**
* Update the notifications
* @param reason why the notifications are updating
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
deleted file mode 100644
index c9c6f28..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.notification;
-
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-
-import java.util.Objects;
-
-/**
- * Root controller for the list of notifications in the shade.
- *
- * TODO: Much of the code in NotificationPresenter should eventually move in here. It will proxy
- * domain-specific behavior (ARC, etc) to subcontrollers.
- */
-public class NotificationListController {
- private final NotificationEntryManager mEntryManager;
- private final NotificationListContainer mListContainer;
- private final DeviceProvisionedController mDeviceProvisionedController;
-
- public NotificationListController(
- NotificationEntryManager entryManager,
- NotificationListContainer listContainer,
- DeviceProvisionedController deviceProvisionedController) {
- mEntryManager = Objects.requireNonNull(entryManager);
- mListContainer = Objects.requireNonNull(listContainer);
- mDeviceProvisionedController = Objects.requireNonNull(deviceProvisionedController);
- }
-
- /**
- * Causes the controller to register listeners on its dependencies. This method must be called
- * before the controller is ready to perform its duties.
- */
- public void bind() {
- mEntryManager.addNotificationEntryListener(mEntryListener);
- mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
- }
-
- @SuppressWarnings("FieldCanBeLocal")
- private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
- @Override
- public void onEntryRemoved(
- NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- mListContainer.cleanUpViewStateForEntry(entry);
- }
- };
-
- // TODO: (b/145659174) remove after moving to NewNotifPipeline. Replaced by
- // DeviceProvisionedCoordinator
- private final DeviceProvisionedListener mDeviceProvisionedListener =
- new DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- mEntryManager.updateNotifications("device provisioned changed");
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index dbf4810..126a986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -18,10 +18,8 @@
import android.animation.ObjectAnimator
import android.util.FloatProperty
-import com.android.systemui.Dumpable
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -34,20 +32,17 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
-import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.min
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
- dumpManager: DumpManager,
private val mHeadsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
private val screenOffAnimationController: ScreenOffAnimationController
-) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener,
- Dumpable {
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
"notificationVisibility") {
@@ -65,7 +60,6 @@
private var mLinearDozeAmount: Float = 0.0f
private var mDozeAmount: Float = 0.0f
- private var mDozeAmountSource: String = "init"
private var mNotificationVisibleAmount = 0.0f
private var mNotificationsVisible = false
private var mNotificationsVisibleForExpansion = false
@@ -148,7 +142,6 @@
}
init {
- dumpManager.registerDumpable(this)
mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
addListener(object : WakeUpListener {
@@ -255,14 +248,13 @@
// Let's notify the scroller that an animation started
notifyAnimationStart(mLinearDozeAmount == 1.0f)
}
- setDozeAmount(linear, eased, source = "StatusBar")
+ setDozeAmount(linear, eased)
}
- fun setDozeAmount(linear: Float, eased: Float, source: String) {
+ fun setDozeAmount(linear: Float, eased: Float) {
val changed = linear != mLinearDozeAmount
mLinearDozeAmount = linear
mDozeAmount = eased
- mDozeAmountSource = source
mStackScrollerController.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
@@ -279,7 +271,7 @@
// undefined state, so it's an indication that we should do state cleanup. We override
// the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
// See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
- setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)")
+ setDozeAmount(0f, 0f)
}
if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) {
@@ -319,11 +311,12 @@
*/
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
- if (statusBarStateController.state == StatusBarState.KEYGUARD) {
- setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
- } else {
- setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
+ var amount = 1.0f
+ if (statusBarStateController.state == StatusBarState.SHADE ||
+ statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
+ amount = 0.0f
}
+ setDozeAmount(amount, amount)
return true
}
return false
@@ -339,7 +332,7 @@
*/
private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
- setDozeAmount(1f, 1f, source = "Override: animating screen off")
+ setDozeAmount(1f, 1f)
return true
}
@@ -433,24 +426,4 @@
*/
@JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
}
-
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("mLinearDozeAmount: $mLinearDozeAmount")
- pw.println("mDozeAmount: $mDozeAmount")
- pw.println("mDozeAmountSource: $mDozeAmountSource")
- pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
- pw.println("mNotificationsVisible: $mNotificationsVisible")
- pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
- pw.println("mVisibilityAmount: $mVisibilityAmount")
- pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
- pw.println("pulseExpanding: $pulseExpanding")
- pw.println("state: ${StatusBarState.toString(state)}")
- pw.println("fullyAwake: $fullyAwake")
- pw.println("wakingUp: $wakingUp")
- pw.println("willWakeUp: $willWakeUp")
- pw.println("collapsedEnoughToHide: $collapsedEnoughToHide")
- pw.println("pulsing: $pulsing")
- pw.println("notificationsFullyHidden: $notificationsFullyHidden")
- pw.println("canShowPulsingHuns: $canShowPulsingHuns")
- }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index 46b467e..d52f3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -16,9 +16,8 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
-import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -35,23 +34,11 @@
*/
void inflateViews(
NotificationEntry entry,
- NotifInflater.Params params,
+ @NonNull NotifInflater.Params params,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException;
/**
- * Called when the ranking has been updated (but not add or remove has been done). The binder
- * should inspect the old and new adjustments and re-inflate the entry's views if necessary
- * (e.g. if something important changed).
- */
- void onNotificationRankingUpdated(
- NotificationEntry entry,
- @Nullable Integer oldImportance,
- NotificationUiAdjustment oldAdjustment,
- NotificationUiAdjustment newAdjustment,
- NotificationRowContentBinder.InflationCallback callback);
-
- /**
* Called when a notification is no longer likely to be displayed and can have its views freed.
*/
void releaseViews(NotificationEntry entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 528f720..47cdde4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -22,6 +22,7 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Build;
@@ -32,12 +33,9 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
@@ -68,8 +66,6 @@
private final ExpandableNotificationRowComponent.Builder
mExpandableNotificationRowComponentBuilder;
private final IconManager mIconManager;
- private final LowPriorityInflationHelper mLowPriorityInflationHelper;
- private final NotifPipelineFlags mNotifPipelineFlags;
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
@@ -86,9 +82,7 @@
RowContentBindStage rowContentBindStage,
Provider<RowInflaterTask> rowInflaterTaskProvider,
ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
- IconManager iconManager,
- LowPriorityInflationHelper lowPriorityInflationHelper,
- NotifPipelineFlags notifPipelineFlags) {
+ IconManager iconManager) {
mContext = context;
mNotifBindPipeline = notifBindPipeline;
mRowContentBindStage = rowContentBindStage;
@@ -98,8 +92,6 @@
mRowInflaterTaskProvider = rowInflaterTaskProvider;
mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
mIconManager = iconManager;
- mLowPriorityInflationHelper = lowPriorityInflationHelper;
- mNotifPipelineFlags = notifPipelineFlags;
}
/**
@@ -125,13 +117,9 @@
@Override
public void inflateViews(
NotificationEntry entry,
- NotifInflater.Params params,
+ @NonNull NotifInflater.Params params,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException {
- if (params == null) {
- // weak assert that the params should always be passed in the new pipeline
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- }
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
if (entry.rowExists()) {
@@ -191,39 +179,6 @@
}
/**
- * Updates the views bound to an entry when the entry's ranking changes, either in-place or by
- * reinflating them.
- *
- * TODO: Should this method be in this class?
- */
- @Override
- public void onNotificationRankingUpdated(
- NotificationEntry entry,
- @Nullable Integer oldImportance,
- NotificationUiAdjustment oldAdjustment,
- NotificationUiAdjustment newAdjustment,
- NotificationRowContentBinder.InflationCallback callback) {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
- if (entry.rowExists()) {
- ExpandableNotificationRow row = entry.getRow();
- row.reset();
- updateRow(entry, row);
- inflateContentViews(entry, null, row, callback);
- } else {
- // Once the RowInflaterTask is done, it will pick up the updated entry, so
- // no-op here.
- }
- } else {
- if (oldImportance != null && entry.getImportance() != oldImportance) {
- if (entry.rowExists()) {
- entry.getRow().onNotificationRankingUpdated();
- }
- }
- }
- }
-
- /**
* Update row after the notification has updated.
*
* @param entry notification that has updated
@@ -243,24 +198,12 @@
*/
private void inflateContentViews(
NotificationEntry entry,
- NotifInflater.Params inflaterParams,
+ @NonNull NotifInflater.Params inflaterParams,
ExpandableNotificationRow row,
@Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
final boolean useIncreasedCollapsedHeight =
mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance());
- final boolean isLowPriority;
- if (inflaterParams != null) {
- // NEW pipeline
- isLowPriority = inflaterParams.isLowPriority();
- } else {
- // LEGACY pipeline
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- // If this is our first time inflating, we don't actually know the groupings for real
- // yet, so we might actually inflate a low priority content view incorrectly here and
- // have to correct it later in the pipeline. On subsequent inflations (i.e. updates),
- // this should inflate the correct view.
- isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
- }
+ final boolean isLowPriority = inflaterParams.isLowPriority();
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
deleted file mode 100644
index 89445a5..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 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.systemui.statusbar.notification.collection.legacy;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import javax.inject.Inject;
-
-/**
- * Helper class that provide methods to help check when we need to inflate a low priority version
- * ot notification content.
- */
-@SysUISingleton
-public class LowPriorityInflationHelper {
- private final NotificationGroupManagerLegacy mGroupManager;
- private final NotifPipelineFlags mNotifPipelineFlags;
-
- @Inject
- LowPriorityInflationHelper(
- NotificationGroupManagerLegacy groupManager,
- NotifPipelineFlags notifPipelineFlags) {
- mGroupManager = groupManager;
- mNotifPipelineFlags = notifPipelineFlags;
- }
-
- /**
- * Whether the notification should inflate a low priority version of its content views.
- */
- public boolean shouldUseLowPriorityView(NotificationEntry entry) {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return entry.isAmbient() && !mGroupManager.isChildInGroup(entry);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
deleted file mode 100644
index d41f6fe..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ /dev/null
@@ -1,950 +0,0 @@
-/*
- * Copyright (C) 2015 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.systemui.statusbar.notification.collection.legacy;
-
-import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.util.Compile;
-import com.android.wm.shell.bubbles.Bubbles;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.TreeSet;
-import java.util.function.Function;
-
-import javax.inject.Inject;
-
-import dagger.Lazy;
-
-/**
- * A class to handle notifications and their corresponding groups.
- * This includes:
- * 1. Determining whether an entry is a member of a group and whether it is a summary or a child
- * 2. Tracking group expansion states
- */
-@SysUISingleton
-public class NotificationGroupManagerLegacy implements StateListener, Dumpable {
-
- private static final String TAG = "LegacyNotifGroupManager";
- private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
- /**
- * The maximum amount of time (in ms) between the posting of notifications that can be
- * considered part of the same update batch.
- */
- private static final long POST_BATCH_MAX_AGE = 5000;
- private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
- private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
- private final Optional<Bubbles> mBubblesOptional;
- private final GroupEventDispatcher mEventDispatcher = new GroupEventDispatcher(mGroupMap::get);
- private final HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
- private boolean mIsUpdatingUnchangedGroup;
-
- @Inject
- public NotificationGroupManagerLegacy(
- StatusBarStateController statusBarStateController,
- Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier,
- Optional<Bubbles> bubblesOptional,
- DumpManager dumpManager) {
- statusBarStateController.addCallback(this);
- mPeopleNotificationIdentifier = peopleNotificationIdentifier;
- mBubblesOptional = bubblesOptional;
-
- dumpManager.registerDumpable(this);
- }
-
- /**
- * Add a listener for changes to groups.
- */
- public void registerGroupChangeListener(OnGroupChangeListener listener) {
- mEventDispatcher.registerGroupChangeListener(listener);
- }
-
- private void setGroupExpanded(NotificationGroup group, boolean expanded) {
- group.expanded = expanded;
- }
-
- /**
- * When we want to remove an entry from being tracked for grouping
- */
- public void onEntryRemoved(NotificationEntry removed) {
- if (SPEW) {
- Log.d(TAG, "onEntryRemoved: entry=" + logKey(removed));
- }
- mEventDispatcher.openBufferScope();
- onEntryRemovedInternal(removed, removed.getSbn());
- StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
- if (oldSbn != null) {
- updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
- }
- mEventDispatcher.closeBufferScope();
- }
-
- /**
- * An entry was removed.
- *
- * @param removed the removed entry
- * @param sbn the notification the entry has, which doesn't need to be the same as it's internal
- * notification
- */
- private void onEntryRemovedInternal(NotificationEntry removed,
- final StatusBarNotification sbn) {
- onEntryRemovedInternal(removed, sbn.getGroupKey(), sbn.isGroup(),
- sbn.getNotification().isGroupSummary());
- }
-
- private void onEntryRemovedInternal(NotificationEntry removed, String notifGroupKey, boolean
- isGroup, boolean isGroupSummary) {
- String groupKey = getGroupKey(removed.getKey(), notifGroupKey);
- final NotificationGroup group = mGroupMap.get(groupKey);
- if (group == null) {
- // When an app posts 2 different notifications as summary of the same group, then a
- // cancellation of the first notification removes this group.
- // This situation is not supported and we will not allow such notifications anymore in
- // the close future. See b/23676310 for reference.
- return;
- }
- if (SPEW) {
- Log.d(TAG, "onEntryRemovedInternal: entry=" + logKey(removed)
- + " group=" + logGroupKey(group));
- }
- if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
- group.children.remove(removed.getKey());
- } else {
- group.summary = null;
- }
- updateSuppression(group);
- if (group.children.isEmpty()) {
- if (group.summary == null) {
- mGroupMap.remove(groupKey);
- mEventDispatcher.notifyGroupRemoved(group);
- }
- }
- }
-
- private void onEntryAddedInternal(final NotificationEntry added) {
- if (added.isRowRemoved()) {
- added.setDebugThrowable(new Throwable());
- }
- final StatusBarNotification sbn = added.getSbn();
- boolean isGroupChild = isGroupChild(sbn);
- String groupKey = getGroupKey(sbn);
- NotificationGroup group = mGroupMap.get(groupKey);
- if (group == null) {
- group = new NotificationGroup(groupKey);
- mGroupMap.put(groupKey, group);
- mEventDispatcher.notifyGroupCreated(group);
- }
- if (SPEW) {
- Log.d(TAG, "onEntryAddedInternal: entry=" + logKey(added)
- + " group=" + logGroupKey(group));
- }
- if (isGroupChild) {
- NotificationEntry existing = group.children.get(added.getKey());
- if (existing != null && existing != added) {
- Throwable existingThrowable = existing.getDebugThrowable();
- Log.wtf(TAG, "Inconsistent entries found with the same key " + logKey(added)
- + "existing removed: " + existing.isRowRemoved()
- + (existingThrowable != null
- ? Log.getStackTraceString(existingThrowable) + "\n" : "")
- + " added removed" + added.isRowRemoved(), new Throwable());
- }
- group.children.put(added.getKey(), added);
- addToPostBatchHistory(group, added);
- updateSuppression(group);
- } else {
- group.summary = added;
- addToPostBatchHistory(group, added);
- group.expanded = added.areChildrenExpanded();
- updateSuppression(group);
- if (!group.children.isEmpty()) {
- ArrayList<NotificationEntry> childrenCopy =
- new ArrayList<>(group.children.values());
- for (NotificationEntry child : childrenCopy) {
- onEntryBecomingChild(child);
- }
- mEventDispatcher.notifyGroupsChanged();
- }
- }
- }
-
- private void addToPostBatchHistory(NotificationGroup group, @Nullable NotificationEntry entry) {
- if (entry == null) {
- return;
- }
- boolean didAdd = group.postBatchHistory.add(new PostRecord(entry));
- if (didAdd) {
- trimPostBatchHistory(group.postBatchHistory);
- }
- }
-
- /** remove all history that's too old to be in the batch. */
- private void trimPostBatchHistory(@NonNull TreeSet<PostRecord> postBatchHistory) {
- if (postBatchHistory.size() <= 1) {
- return;
- }
- long batchStartTime = postBatchHistory.last().postTime - POST_BATCH_MAX_AGE;
- while (!postBatchHistory.isEmpty() && postBatchHistory.first().postTime < batchStartTime) {
- postBatchHistory.pollFirst();
- }
- }
-
- private void onEntryBecomingChild(NotificationEntry entry) {
- updateIsolation(entry);
- }
-
- private void updateSuppression(NotificationGroup group) {
- if (group == null) {
- return;
- }
- NotificationEntry prevAlertOverride = group.alertOverride;
- group.alertOverride = getPriorityConversationAlertOverride(group);
-
- int childCount = 0;
- boolean hasBubbles = false;
- for (NotificationEntry entry : group.children.values()) {
- if (mBubblesOptional.isPresent() && mBubblesOptional.get()
- .isBubbleNotificationSuppressedFromShade(
- entry.getKey(), entry.getSbn().getGroupKey())) {
- hasBubbles = true;
- } else {
- childCount++;
- }
- }
-
- boolean prevSuppressed = group.suppressed;
- group.suppressed = group.summary != null && !group.expanded
- && (childCount == 1
- || (childCount == 0
- && group.summary.getSbn().getNotification().isGroupSummary()
- && (hasIsolatedChildren(group) || hasBubbles)));
-
- boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
- boolean suppressionChanged = prevSuppressed != group.suppressed;
- if (alertOverrideChanged || suppressionChanged) {
- if (DEBUG) {
- Log.d(TAG, "updateSuppression:"
- + " willNotifyListeners=" + !mIsUpdatingUnchangedGroup
- + " changes for group:\n" + group);
- if (alertOverrideChanged) {
- Log.d(TAG, "updateSuppression: alertOverride was=" + logKey(prevAlertOverride)
- + " now=" + logKey(group.alertOverride));
- }
- if (suppressionChanged) {
- Log.d(TAG, "updateSuppression: suppressed changed to " + group.suppressed);
- }
- }
- if (alertOverrideChanged) {
- mEventDispatcher.notifyAlertOverrideChanged(group, prevAlertOverride);
- }
- if (suppressionChanged) {
- mEventDispatcher.notifySuppressedChanged(group);
- }
- if (!mIsUpdatingUnchangedGroup) {
- mEventDispatcher.notifyGroupsChanged();
- }
- }
- }
-
- /**
- * Finds the isolated logical child of this group which is should be alerted instead.
- *
- * Notifications from priority conversations are isolated from their groups to make them more
- * prominent, however apps may post these with a GroupAlertBehavior that has the group receiving
- * the alert. This would lead to the group alerting even though the conversation that was
- * updated was not actually a part of that group. This method finds the best priority
- * conversation in this situation, if there is one, so they can be set as the alertOverride of
- * the group.
- *
- * @param group the group to check
- * @return the entry which should receive the alert instead of the group, if any.
- */
- @Nullable
- private NotificationEntry getPriorityConversationAlertOverride(NotificationGroup group) {
- // GOAL: if there is a priority child which wouldn't alert based on its groupAlertBehavior,
- // but which should be alerting (because priority conversations are isolated), find it.
- if (group == null || group.summary == null) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary"
- + " group=" + logGroupKey(group));
- }
- return null;
- }
- if (isIsolated(group.summary.getKey())) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: isolated group"
- + " group=" + logGroupKey(group));
- }
- return null;
- }
-
- // Precondiions:
- // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
- // * Only necessary when at least one notification in the group is on a priority channel
- if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
- == Notification.GROUP_ALERT_CHILDREN) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: summary == GROUP_ALERT_CHILDREN"
- + " group=" + logGroupKey(group));
- }
- return null;
- }
-
- // Get the important children first, copy the keys for the final importance check,
- // then add the non-isolated children to the map for unified lookup.
- HashMap<String, NotificationEntry> children = getImportantConversations(group);
- if (children == null || children.isEmpty()) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations"
- + " group=" + logGroupKey(group));
- }
- return null;
- }
- HashSet<String> importantChildKeys = new HashSet<>(children.keySet());
- children.putAll(group.children);
-
- // Ensure all children have GROUP_ALERT_SUMMARY
- for (NotificationEntry child : children.values()) {
- if (child.getSbn().getNotification().getGroupAlertBehavior()
- != Notification.GROUP_ALERT_SUMMARY) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: child != GROUP_ALERT_SUMMARY"
- + " group=" + logGroupKey(group));
- }
- return null;
- }
- }
-
- // Create a merged post history from all the children
- TreeSet<PostRecord> combinedHistory = new TreeSet<>(group.postBatchHistory);
- for (String importantChildKey : importantChildKeys) {
- NotificationGroup importantChildGroup = mGroupMap.get(importantChildKey);
- combinedHistory.addAll(importantChildGroup.postBatchHistory);
- }
- trimPostBatchHistory(combinedHistory);
-
- // This is a streamlined implementation of the following idea:
- // * From the subset of notifications in the latest 'batch' of updates. A batch is:
- // * Notifs posted less than POST_BATCH_MAX_AGE before the most recently posted.
- // * Only including notifs newer than the second-to-last post of any notification.
- // * Find the newest child in the batch -- the with the largest 'when' value.
- // * If the newest child is a priority conversation, set that as the override.
- HashSet<String> batchKeys = new HashSet<>();
- long newestChildWhen = -1;
- NotificationEntry newestChild = null;
- // Iterate backwards through the post history, tracking the child with the smallest sort key
- for (PostRecord record : combinedHistory.descendingSet()) {
- if (batchKeys.contains(record.key)) {
- // Once you see a notification again, the batch has ended
- break;
- }
- batchKeys.add(record.key);
- NotificationEntry child = children.get(record.key);
- if (child != null) {
- long childWhen = child.getSbn().getNotification().when;
- if (newestChild == null || childWhen > newestChildWhen) {
- newestChildWhen = childWhen;
- newestChild = child;
- }
- }
- }
- if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride:"
- + " result=" + logKey(newestChild)
- + " group=" + logGroupKey(group));
- }
- return newestChild;
- }
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride:"
- + " result=null newestChild=" + logKey(newestChild)
- + " group=" + logGroupKey(group));
- }
- return null;
- }
-
- private boolean hasIsolatedChildren(NotificationGroup group) {
- return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
- }
-
- private int getNumberOfIsolatedChildren(String groupKey) {
- int count = 0;
- for (StatusBarNotification sbn : mIsolatedEntries.values()) {
- if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) {
- count++;
- }
- }
- return count;
- }
-
- @Nullable
- private HashMap<String, NotificationEntry> getImportantConversations(NotificationGroup group) {
- String groupKey = group.summary.getSbn().getGroupKey();
- HashMap<String, NotificationEntry> result = null;
- for (StatusBarNotification sbn : mIsolatedEntries.values()) {
- if (sbn.getGroupKey().equals(groupKey)) {
- NotificationEntry entry = mGroupMap.get(sbn.getKey()).summary;
- if (isImportantConversation(entry)) {
- if (result == null) {
- result = new HashMap<>();
- }
- result.put(sbn.getKey(), entry);
- }
- }
- }
- return result;
- }
-
- private void setStatusBarState(int newState) {
- if (newState == StatusBarState.KEYGUARD) {
- collapseGroups();
- }
- }
-
- private void collapseGroups() {
- // Because notifications can become isolated when the group becomes suppressed it can
- // lead to concurrent modifications while looping. We need to make a copy.
- ArrayList<NotificationGroup> groupCopy = new ArrayList<>(mGroupMap.values());
- int size = groupCopy.size();
- for (int i = 0; i < size; i++) {
- NotificationGroup group = groupCopy.get(i);
- if (group.expanded) {
- setGroupExpanded(group, false);
- }
- updateSuppression(group);
- }
- }
-
- public boolean isChildInGroup(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.getSbn();
- if (!isGroupChild(sbn)) {
- return false;
- }
- NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
- if (group == null || group.summary == null || group.suppressed) {
- return false;
- }
- if (group.children.isEmpty()) {
- // If the suppression of a group changes because the last child was removed, this can
- // still be called temporarily because the child hasn't been fully removed yet. Let's
- // make sure we still return false in that case.
- return false;
- }
- return true;
- }
-
- /**
- * If there is a {@link NotificationGroup} associated with the provided entry, this method
- * will update the suppression of that group.
- */
- public void updateSuppression(NotificationEntry entry) {
- NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
- if (group != null) {
- updateSuppression(group);
- }
- }
-
- /**
- * Get the group key. May differ from the one in the notification due to the notification
- * being temporarily isolated.
- *
- * @param sbn notification to check
- * @return the key of the notification
- */
- private String getGroupKey(StatusBarNotification sbn) {
- return getGroupKey(sbn.getKey(), sbn.getGroupKey());
- }
-
- private String getGroupKey(String key, String groupKey) {
- if (isIsolated(key)) {
- return key;
- }
- return groupKey;
- }
-
- private boolean isIsolated(String sbnKey) {
- return mIsolatedEntries.containsKey(sbnKey);
- }
-
- /**
- * Whether a notification is visually a group child.
- *
- * @param sbn notification to check
- * @return true if it is visually a group child
- */
- private boolean isGroupChild(StatusBarNotification sbn) {
- return isGroupChild(sbn.getKey(), sbn.isGroup(), sbn.getNotification().isGroupSummary());
- }
-
- private boolean isGroupChild(String key, boolean isGroup, boolean isGroupSummary) {
- if (isIsolated(key)) {
- return false;
- }
- return isGroup && !isGroupSummary;
- }
-
- /**
- * Whether a notification that is normally part of a group should be temporarily isolated from
- * the group and put in their own group visually. This generally happens when the notification
- * is alerting.
- *
- * @param entry the notification to check
- * @return true if the entry should be isolated
- */
- private boolean shouldIsolate(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
- if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
- return false;
- }
- if (isImportantConversation(entry)) {
- return true;
- }
- NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
- return (sbn.getNotification().fullScreenIntent != null
- || notificationGroup == null
- || !notificationGroup.expanded
- || isGroupNotFullyVisible(notificationGroup));
- }
-
- private boolean isImportantConversation(NotificationEntry entry) {
- int peopleNotificationType =
- mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
- return peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON;
- }
-
- /**
- * Isolate a notification from its group so that it visually shows as its own group.
- *
- * @param entry the notification to isolate
- */
- private void isolateNotification(NotificationEntry entry) {
- if (SPEW) {
- Log.d(TAG, "isolateNotification: entry=" + logKey(entry));
- }
- // We will be isolated now, so lets update the groups
- onEntryRemovedInternal(entry, entry.getSbn());
-
- mIsolatedEntries.put(entry.getKey(), entry.getSbn());
-
- onEntryAddedInternal(entry);
- // We also need to update the suppression of the old group, because this call comes
- // even before the groupManager knows about the notification at all.
- // When the notification gets added afterwards it is already isolated and therefore
- // it doesn't lead to an update.
- updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey()));
- mEventDispatcher.notifyGroupsChanged();
- }
-
- /**
- * Update the isolation of an entry, splitting it from the group.
- */
- private void updateIsolation(NotificationEntry entry) {
- // We need to buffer a few events because we do isolation changes in 3 steps:
- // removeInternal, update mIsolatedEntries, addInternal. This means that often the
- // alertOverride will update on the removal, however processing the event in that case can
- // cause problems because the mIsolatedEntries map is not in its final state, so the event
- // listener may be unable to correctly determine the true state of the group. By delaying
- // the alertOverride change until after the add phase, we can ensure that listeners only
- // have to handle a consistent state.
- mEventDispatcher.openBufferScope();
- boolean isIsolated = isIsolated(entry.getSbn().getKey());
- if (shouldIsolate(entry)) {
- if (!isIsolated) {
- isolateNotification(entry);
- }
- } else if (isIsolated) {
- stopIsolatingNotification(entry);
- }
- mEventDispatcher.closeBufferScope();
- }
-
- /**
- * Stop isolating a notification and re-group it with its original logical group.
- *
- * @param entry the notification to un-isolate
- */
- private void stopIsolatingNotification(NotificationEntry entry) {
- if (SPEW) {
- Log.d(TAG, "stopIsolatingNotification: entry=" + logKey(entry));
- }
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.remove(entry.getKey());
- onEntryAddedInternal(entry);
- mEventDispatcher.notifyGroupsChanged();
- }
-
- private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
- return notificationGroup.summary == null
- || notificationGroup.summary.isGroupNotFullyVisible();
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println("GroupManagerLegacy state:");
- pw.println(" number of groups: " + mGroupMap.size());
- for (Map.Entry<String, NotificationGroup> entry : mGroupMap.entrySet()) {
- pw.println("\n key: " + logKey(entry.getKey())); pw.println(entry.getValue());
- }
- pw.println("\n isolated entries: " + mIsolatedEntries.size());
- for (Map.Entry<String, StatusBarNotification> entry : mIsolatedEntries.entrySet()) {
- pw.print(" "); pw.print(logKey(entry.getKey()));
- pw.print(", "); pw.println(entry.getValue());
- }
- }
-
- @Override
- public void onStateChanged(int newState) {
- setStatusBarState(newState);
- }
-
- /** Get the group key, reformatted for logging, for the (optional) group */
- private static String logGroupKey(NotificationGroup group) {
- if (group == null) {
- return "null";
- }
- return logKey(group.groupKey);
- }
-
- /**
- * A record of a notification being posted, containing the time of the post and the key of the
- * notification entry. These are stored in a TreeSet by the NotificationGroup and used to
- * calculate a batch of notifications.
- */
- public static class PostRecord implements Comparable<PostRecord> {
- public final long postTime;
- public final String key;
-
- /** constructs a record containing the post time and key from the notification entry */
- public PostRecord(@NonNull NotificationEntry entry) {
- this.postTime = entry.getSbn().getPostTime();
- this.key = entry.getKey();
- }
-
- @Override
- public int compareTo(PostRecord o) {
- int postTimeComparison = Long.compare(this.postTime, o.postTime);
- return postTimeComparison == 0
- ? String.CASE_INSENSITIVE_ORDER.compare(this.key, o.key)
- : postTimeComparison;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PostRecord that = (PostRecord) o;
- return postTime == that.postTime && key.equals(that.key);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(postTime, key);
- }
- }
-
- /**
- * Represents a notification group in the notification shade.
- */
- public static class NotificationGroup {
- public final String groupKey;
- public final HashMap<String, NotificationEntry> children = new HashMap<>();
- public final TreeSet<PostRecord> postBatchHistory = new TreeSet<>();
- public NotificationEntry summary;
- public boolean expanded;
- /**
- * Is this notification group suppressed, i.e its summary is hidden
- */
- public boolean suppressed;
- /**
- * The child (which is isolated from this group) to which the alert should be transferred,
- * due to priority conversations.
- */
- public NotificationEntry alertOverride;
-
- NotificationGroup(String groupKey) {
- this.groupKey = groupKey;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(" groupKey: ").append(groupKey);
- sb.append("\n summary:");
- appendEntry(sb, summary);
- sb.append("\n children size: ").append(children.size());
- for (NotificationEntry child : children.values()) {
- appendEntry(sb, child);
- }
- sb.append("\n alertOverride:");
- appendEntry(sb, alertOverride);
- sb.append("\n summary suppressed: ").append(suppressed);
- return sb.toString();
- }
-
- private void appendEntry(StringBuilder sb, NotificationEntry entry) {
- sb.append("\n ").append(entry != null ? entry.getSbn() : "null");
- if (entry != null && entry.getDebugThrowable() != null) {
- sb.append(Log.getStackTraceString(entry.getDebugThrowable()));
- }
- }
- }
-
- /**
- * This class is a toggleable buffer for a subset of events of {@link OnGroupChangeListener}.
- * When buffering, instead of notifying the listeners it will set internal state that will allow
- * it to notify listeners of those events later
- */
- static class GroupEventDispatcher {
- private final Function<String, NotificationGroup> mGroupMapGetter;
- private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
- private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
- private final HashMap<String, Boolean> mOldSuppressedByGroup = new HashMap<>();
- private int mBufferScopeDepth = 0;
- private boolean mDidGroupsChange = false;
-
- GroupEventDispatcher(Function<String, NotificationGroup> groupMapGetter) {
- mGroupMapGetter = requireNonNull(groupMapGetter);
- }
-
- void registerGroupChangeListener(OnGroupChangeListener listener) {
- mGroupChangeListeners.add(listener);
- }
-
- private boolean isBuffering() {
- return mBufferScopeDepth > 0;
- }
-
- void notifyAlertOverrideChanged(NotificationGroup group,
- NotificationEntry oldAlertOverride) {
- if (isBuffering()) {
- // The value in this map is the override before the event. If there is an entry
- // already in the map, then we are effectively coalescing two events, which means
- // we need to preserve the original initial value.
- if (!mOldAlertOverrideByGroup.containsKey(group.groupKey)) {
- mOldAlertOverrideByGroup.put(group.groupKey, oldAlertOverride);
- }
- } else {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
- group.alertOverride);
- }
- }
- }
-
- void notifySuppressedChanged(NotificationGroup group) {
- if (isBuffering()) {
- // The value in this map is the 'suppressed' before the event. If there is a value
- // already in the map, then we are effectively coalescing two events, which means
- // we need to preserve the original initial value.
- mOldSuppressedByGroup.putIfAbsent(group.groupKey, !group.suppressed);
- } else {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupSuppressionChanged(group, group.suppressed);
- }
- }
- }
-
- void notifyGroupsChanged() {
- if (isBuffering()) {
- mDidGroupsChange = true;
- } else {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
- }
- }
- }
-
- void notifyGroupCreated(NotificationGroup group) {
- // NOTE: given how this event is used, it doesn't need to be buffered right now
- final String groupKey = group.groupKey;
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupCreated(group, groupKey);
- }
- }
-
- void notifyGroupRemoved(NotificationGroup group) {
- // NOTE: given how this event is used, it doesn't need to be buffered right now
- final String groupKey = group.groupKey;
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupRemoved(group, groupKey);
- }
- }
-
- void openBufferScope() {
- mBufferScopeDepth++;
- if (SPEW) {
- Log.d(TAG, "openBufferScope: scopeDepth=" + mBufferScopeDepth);
- }
- }
-
- void closeBufferScope() {
- mBufferScopeDepth--;
- if (SPEW) {
- Log.d(TAG, "closeBufferScope: scopeDepth=" + mBufferScopeDepth);
- }
- // Flush buffered events if the last buffer scope has closed
- if (!isBuffering()) {
- flushBuffer();
- }
- }
-
- private void flushBuffer() {
- if (SPEW) {
- Log.d(TAG, "flushBuffer: "
- + " suppressed.size=" + mOldSuppressedByGroup.size()
- + " alertOverride.size=" + mOldAlertOverrideByGroup.size()
- + " mDidGroupsChange=" + mDidGroupsChange);
- }
- // alert all group suppressed changes for groups that were not removed
- for (Map.Entry<String, Boolean> entry : mOldSuppressedByGroup.entrySet()) {
- NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
- if (group == null) {
- // The group can be null if this suppressed changed before the group was
- // permanently removed, meaning that there's no guarantee that listeners will
- // that field clear.
- if (SPEW) {
- Log.d(TAG, "flushBuffer: suppressed:"
- + " cannot report for removed group: " + logKey(entry.getKey()));
- }
- continue;
- }
- boolean oldSuppressed = entry.getValue();
- if (group.suppressed == oldSuppressed) {
- // If the final suppressed equals the initial, it means we coalesced two
- // events which undid the change, so we can drop it entirely.
- if (SPEW) {
- Log.d(TAG, "flushBuffer: suppressed:"
- + " did not change for group: " + logKey(entry.getKey()));
- }
- continue;
- }
- notifySuppressedChanged(group);
- }
- mOldSuppressedByGroup.clear();
- // alert all group alert override changes for groups that were not removed
- for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
- NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
- if (group == null) {
- // The group can be null if this alertOverride changed before the group was
- // permanently removed, meaning that there's no guarantee that listeners will
- // that field clear.
- if (SPEW) {
- Log.d(TAG, "flushBuffer: alertOverride:"
- + " cannot report for removed group: " + entry.getKey());
- }
- continue;
- }
- NotificationEntry oldAlertOverride = entry.getValue();
- if (group.alertOverride == oldAlertOverride) {
- // If the final alertOverride equals the initial, it means we coalesced two
- // events which undid the change, so we can drop it entirely.
- if (SPEW) {
- Log.d(TAG, "flushBuffer: alertOverride:"
- + " did not change for group: " + logKey(entry.getKey()));
- }
- continue;
- }
- notifyAlertOverrideChanged(group, oldAlertOverride);
- }
- mOldAlertOverrideByGroup.clear();
- // alert that groups changed
- if (mDidGroupsChange) {
- notifyGroupsChanged();
- mDidGroupsChange = false;
- }
- }
- }
-
- /**
- * Listener for group changes not including group expansion changes which are handled by
- * {@link OnGroupExpansionChangeListener}.
- */
- public interface OnGroupChangeListener {
- /**
- * A new group has been created.
- *
- * @param group the group that was created
- * @param groupKey the group's key
- */
- default void onGroupCreated(
- NotificationGroup group,
- String groupKey) {}
-
- /**
- * A group has been removed.
- *
- * @param group the group that was removed
- * @param groupKey the group's key
- */
- default void onGroupRemoved(
- NotificationGroup group,
- String groupKey) {}
-
- /**
- * The suppression of a group has changed.
- *
- * @param group the group that has changed
- * @param suppressed true if the group is now suppressed, false o/w
- */
- default void onGroupSuppressionChanged(
- NotificationGroup group,
- boolean suppressed) {}
-
- /**
- * The alert override of a group has changed.
- *
- * @param group the group that has changed
- * @param oldAlertOverride the previous notification to which the group's alerts were sent
- * @param newAlertOverride the notification to which the group's alerts should now be sent
- */
- default void onGroupAlertOverrideChanged(
- NotificationGroup group,
- @Nullable NotificationEntry oldAlertOverride,
- @Nullable NotificationEntry newAlertOverride) {}
-
- /**
- * The groups have changed. This can happen if the isolation of a child has changes or if a
- * group became suppressed / unsuppressed
- */
- default void onGroupsChanged() {}
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java
deleted file mode 100644
index bb8c0e0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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 com.android.systemui.statusbar.notification.collection.legacy;
-
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-
-/**
- * A manager that ensures that notifications are visually stable. It will suppress reorderings
- * and reorder at the right time when they are out of view.
- */
-public class VisualStabilityManager {
-
- private final VisualStabilityProvider mVisualStabilityProvider;
-
- private boolean mPanelExpanded;
- private boolean mScreenOn;
- private boolean mPulsing;
-
- /**
- * Injected constructor. See {@link NotificationsModule}.
- */
- public VisualStabilityManager(
- VisualStabilityProvider visualStabilityProvider,
- StatusBarStateController statusBarStateController,
- WakefulnessLifecycle wakefulnessLifecycle) {
-
- mVisualStabilityProvider = visualStabilityProvider;
-
- if (statusBarStateController != null) {
- setPulsing(statusBarStateController.isPulsing());
- statusBarStateController.addCallback(new StatusBarStateController.StateListener() {
- @Override
- public void onPulsingChanged(boolean pulsing) {
- setPulsing(pulsing);
- }
-
- @Override
- public void onExpandedChanged(boolean expanded) {
- setPanelExpanded(expanded);
- }
- });
- }
-
- if (wakefulnessLifecycle != null) {
- wakefulnessLifecycle.addObserver(mWakefulnessObserver);
- }
- }
-
- /**
- * @param screenOn whether the screen is on
- */
- private void setScreenOn(boolean screenOn) {
- mScreenOn = screenOn;
- updateAllowedStates();
- }
-
- /**
- * Set the panel to be expanded.
- */
- private void setPanelExpanded(boolean expanded) {
- mPanelExpanded = expanded;
- updateAllowedStates();
- }
-
- /**
- * @param pulsing whether we are currently pulsing for ambient display.
- */
- private void setPulsing(boolean pulsing) {
- if (mPulsing == pulsing) {
- return;
- }
- mPulsing = pulsing;
- updateAllowedStates();
- }
-
- private void updateAllowedStates() {
- boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
- mVisualStabilityProvider.setReorderingAllowed(reorderingAllowed);
- }
-
- final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
- @Override
- public void onFinishedGoingToSleep() {
- setScreenOn(false);
- }
-
- @Override
- public void onStartedWakingUp() {
- setScreenOn(true);
- }
- };
-
-
- /**
- * See {@link Callback#onChangeAllowed()}
- */
- public interface Callback {
-
- /**
- * Called when changing is allowed again.
- */
- void onChangeAllowed();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index eda2eec..9333c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -24,22 +24,18 @@
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.NotifPanelEventsModule;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
@@ -53,12 +49,9 @@
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -84,7 +77,6 @@
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
@@ -118,16 +110,8 @@
@SysUISingleton
@Provides
static NotificationEntryManager provideNotificationEntryManager(
- NotificationEntryManagerLogger logger,
- NotificationGroupManagerLegacy groupManager,
- NotifPipelineFlags notifPipelineFlags,
- Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
- LeakDetector leakDetector,
- IStatusBarService statusBarService,
- @Background Executor bgExecutor) {
- return new NotificationEntryManager(
- logger
- );
+ NotificationEntryManagerLogger logger) {
+ return new NotificationEntryManager(logger);
}
/** Provides an instance of {@link NotificationGutsManager} */
@@ -141,7 +125,6 @@
AccessibilityManager accessibilityManager,
HighPriorityProvider highPriorityProvider,
INotificationManager notificationManager,
- NotificationEntryManager notificationEntryManager,
PeopleSpaceWidgetManager peopleSpaceWidgetManager,
LauncherApps launcherApps,
ShortcutManager shortcutManager,
@@ -177,20 +160,6 @@
@Binds
NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
- /** Provides an instance of {@link VisualStabilityManager} */
- @SysUISingleton
- @Provides
- static VisualStabilityManager provideVisualStabilityManager(
- VisualStabilityProvider visualStabilityProvider,
- StatusBarStateController statusBarStateController,
- WakefulnessLifecycle wakefulnessLifecycle) {
- return new VisualStabilityManager(
- visualStabilityProvider,
- statusBarStateController,
- wakefulnessLifecycle
- );
- }
-
/** Provides an instance of {@link NotificationLogger} */
@SysUISingleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 385fbb5..be9f133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -27,20 +27,20 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.NotificationClicker
import com.android.systemui.statusbar.notification.NotificationEntryManager
-import com.android.systemui.statusbar.notification.NotificationListController
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
import java.io.PrintWriter
@@ -65,7 +65,6 @@
private val notifPipelineInitializer: Lazy<NotifPipelineInitializer>,
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val notificationLogger: NotificationLogger,
- private val deviceProvisionedController: DeviceProvisionedController,
private val notificationRowBinder: NotificationRowBinderImpl,
private val notificationsMediaManager: NotificationMediaManager,
private val headsUpViewBinder: HeadsUpViewBinder,
@@ -85,12 +84,11 @@
) {
notificationListener.registerAsSystemService()
- val listController =
- NotificationListController(
- entryManager,
- listContainer,
- deviceProvisionedController)
- listController.bind()
+ notifPipeline.get().addCollectionListener(object : NotifCollectionListener {
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ listContainer.cleanUpViewStateForEntry(entry)
+ }
+ })
notificationRowBinder.setNotificationClicker(
clickerBuilder.build(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt
new file mode 100644
index 0000000..4cf3572
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.systemui.statusbar.notification.people
+
+import android.service.notification.StatusBarNotification
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.NotificationPersonExtractorPlugin
+import com.android.systemui.statusbar.policy.ExtensionController
+import javax.inject.Inject
+
+interface NotificationPersonExtractor {
+ fun isPersonNotification(sbn: StatusBarNotification): Boolean
+}
+
+@SysUISingleton
+class NotificationPersonExtractorPluginBoundary @Inject constructor(
+ extensionController: ExtensionController
+) : NotificationPersonExtractor {
+
+ private var plugin: NotificationPersonExtractorPlugin? = null
+
+ init {
+ plugin = extensionController
+ .newExtension(NotificationPersonExtractorPlugin::class.java)
+ .withPlugin(NotificationPersonExtractorPlugin::class.java)
+ .withCallback { extractor ->
+ plugin = extractor
+ }
+ .build()
+ .get()
+ }
+
+ override fun isPersonNotification(sbn: StatusBarNotification): Boolean =
+ plugin?.isPersonNotification(sbn) ?: false
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
deleted file mode 100644
index 3af6ba8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.notification.people
-
-import android.graphics.drawable.Drawable
-
-/**
- * `ViewModel` for PeopleHub view.
- *
- * @param people ViewModels for individual people in PeopleHub, in order that they should be
- * displayed
- * @param isVisible Whether or not the whole PeopleHub UI is visible
- **/
-data class PeopleHubViewModel(val people: Sequence<PersonViewModel>, val isVisible: Boolean)
-
-/** `ViewModel` for a single "Person' in PeopleHub. */
-data class PersonViewModel(
- val name: CharSequence,
- val icon: Drawable,
- val onClick: () -> Unit
-)
-
-/**
- * `Model` for PeopleHub.
- *
- * @param people Models for individual people in PeopleHub, in order that they should be displayed
- **/
-data class PeopleHubModel(val people: Collection<PersonModel>)
-
-/** `Model` for a single "Person" in PeopleHub. */
-data class PersonModel(
- val key: PersonKey,
- val userId: Int,
- // TODO: these should live in the ViewModel
- val name: CharSequence,
- val avatar: Drawable,
- val clickRunnable: Runnable
-)
-
-/** Unique identifier for a Person in PeopleHub. */
-typealias PersonKey = String
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
index 16574ab..c17ffb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
@@ -21,25 +21,6 @@
@Module
abstract class PeopleHubModule {
-
- @Binds
- abstract fun peopleHubSectionFooterViewAdapter(
- impl: PeopleHubViewAdapterImpl
- ): PeopleHubViewAdapter
-
- @Binds
- abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
-
- @Binds
- abstract fun peopleHubSettingChangeDataSource(
- impl: PeopleHubSettingChangeDataSourceImpl
- ): DataSource<Boolean>
-
- @Binds
- abstract fun peopleHubViewModelFactoryDataSource(
- impl: PeopleHubViewModelFactoryDataSourceImpl
- ): DataSource<PeopleHubViewModelFactory>
-
@Binds
abstract fun peopleNotificationIdentifier(
impl: PeopleNotificationIdentifierImpl
@@ -49,4 +30,4 @@
abstract fun notificationPersonExtractor(
pluginImpl: NotificationPersonExtractorPluginBoundary
): NotificationPersonExtractor
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
deleted file mode 100644
index 6062941..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.notification.people
-
-import android.app.Notification
-import android.content.Context
-import android.content.pm.LauncherApps
-import android.content.pm.PackageManager
-import android.content.pm.UserInfo
-import android.graphics.drawable.Drawable
-import android.os.UserManager
-import android.service.notification.NotificationListenerService
-import android.service.notification.NotificationListenerService.REASON_SNOOZED
-import android.service.notification.StatusBarNotification
-import android.util.IconDrawableFactory
-import android.util.SparseArray
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import com.android.internal.widget.MessagingGroup
-import com.android.settingslib.notification.ConversationIconFactory
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.plugins.NotificationPersonExtractorPlugin
-import com.android.systemui.statusbar.NotificationListener
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
-import com.android.systemui.statusbar.policy.ExtensionController
-import java.util.ArrayDeque
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-private const val MAX_STORED_INACTIVE_PEOPLE = 10
-
-interface NotificationPersonExtractor {
- fun extractPerson(sbn: StatusBarNotification): PersonModel?
- fun extractPersonKey(sbn: StatusBarNotification): String?
- fun isPersonNotification(sbn: StatusBarNotification): Boolean
-}
-
-@SysUISingleton
-class NotificationPersonExtractorPluginBoundary @Inject constructor(
- extensionController: ExtensionController
-) : NotificationPersonExtractor {
-
- private var plugin: NotificationPersonExtractorPlugin? = null
-
- init {
- plugin = extensionController
- .newExtension(NotificationPersonExtractorPlugin::class.java)
- .withPlugin(NotificationPersonExtractorPlugin::class.java)
- .withCallback { extractor ->
- plugin = extractor
- }
- .build()
- .get()
- }
-
- override fun extractPerson(sbn: StatusBarNotification) =
- plugin?.extractPerson(sbn)?.run {
- PersonModel(key, sbn.user.identifier, name, avatar, clickRunnable)
- }
-
- override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
-
- override fun isPersonNotification(sbn: StatusBarNotification): Boolean =
- plugin?.isPersonNotification(sbn) ?: false
-}
-
-@SysUISingleton
-class PeopleHubDataSourceImpl @Inject constructor(
- private val notifCollection: CommonNotifCollection,
- private val extractor: NotificationPersonExtractor,
- private val userManager: UserManager,
- launcherApps: LauncherApps,
- packageManager: PackageManager,
- context: Context,
- private val notificationListener: NotificationListener,
- @Background private val bgExecutor: Executor,
- @Main private val mainExecutor: Executor,
- private val notifLockscreenUserMgr: NotificationLockscreenUserManager,
- private val peopleNotificationIdentifier: PeopleNotificationIdentifier
-) : DataSource<PeopleHubModel> {
-
- private var userChangeSubscription: Subscription? = null
- private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
- private val peopleHubManagerForUser = SparseArray<PeopleHubManager>()
-
- private val iconFactory = run {
- val appContext = context.applicationContext
- ConversationIconFactory(
- appContext,
- launcherApps,
- packageManager,
- IconDrawableFactory.newInstance(appContext),
- appContext.resources.getDimensionPixelSize(
- R.dimen.notification_guts_conversation_icon_size
- )
- )
- }
-
- private val notifCollectionListener = object : NotifCollectionListener {
- override fun onEntryAdded(entry: NotificationEntry) = addVisibleEntry(entry)
- override fun onEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry)
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) =
- removeVisibleEntry(entry, reason)
- }
-
- private fun removeVisibleEntry(entry: NotificationEntry, reason: Int) {
- (extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey())?.let { key ->
- val userId = entry.sbn.user.identifier
- bgExecutor.execute {
- val parentId = userManager.getProfileParent(userId)?.id ?: userId
- mainExecutor.execute {
- if (reason == REASON_SNOOZED) {
- if (peopleHubManagerForUser[parentId]?.migrateActivePerson(key) == true) {
- updateUi()
- }
- } else {
- peopleHubManagerForUser[parentId]?.removeActivePerson(key)
- }
- }
- }
- }
- }
-
- private fun addVisibleEntry(entry: NotificationEntry) {
- entry.extractPerson()?.let { personModel ->
- val userId = entry.sbn.user.identifier
- bgExecutor.execute {
- val parentId = userManager.getProfileParent(userId)?.id ?: userId
- mainExecutor.execute {
- val manager = peopleHubManagerForUser[parentId]
- ?: PeopleHubManager().also { peopleHubManagerForUser.put(parentId, it) }
- if (manager.addActivePerson(personModel)) {
- updateUi()
- }
- }
- }
- }
- }
-
- override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
- val register = dataListeners.isEmpty()
- dataListeners.add(listener)
- if (register) {
- userChangeSubscription = notifLockscreenUserMgr.registerListener(
- object : NotificationLockscreenUserManager.UserChangedListener {
- override fun onUserChanged(userId: Int) = updateUi()
- override fun onCurrentProfilesChanged(
- currentProfiles: SparseArray<UserInfo>?
- ) = updateUi()
- })
- notifCollection.addCollectionListener(notifCollectionListener)
- } else {
- getPeopleHubModelForCurrentUser()?.let(listener::onDataChanged)
- }
- return object : Subscription {
- override fun unsubscribe() {
- dataListeners.remove(listener)
- if (dataListeners.isEmpty()) {
- userChangeSubscription?.unsubscribe()
- userChangeSubscription = null
- notifCollection.removeCollectionListener(notifCollectionListener)
- }
- }
- }
- }
-
- private fun getPeopleHubModelForCurrentUser(): PeopleHubModel? {
- val currentUserId = notifLockscreenUserMgr.currentUserId
- val model = peopleHubManagerForUser[currentUserId]?.getPeopleHubModel()
- ?: return null
- val currentProfiles = notifLockscreenUserMgr.currentProfiles
- return model.copy(people = model.people.filter { person ->
- currentProfiles[person.userId]?.isQuietModeEnabled == false
- })
- }
-
- private fun updateUi() {
- val model = getPeopleHubModelForCurrentUser() ?: return
- for (listener in dataListeners) {
- listener.onDataChanged(model)
- }
- }
-
- private fun NotificationEntry.extractPerson(): PersonModel? {
- val type = peopleNotificationIdentifier.getPeopleNotificationType(this)
- if (type == TYPE_NON_PERSON) {
- return null
- }
- val clickRunnable = Runnable { notificationListener.unsnoozeNotification(key) }
- val extras = sbn.notification.extras
- val name = ranking.conversationShortcutInfo?.label
- ?: extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE)
- ?: extras.getCharSequence(Notification.EXTRA_TITLE)
- ?: return null
- val drawable = ranking.getIcon(iconFactory, sbn)
- ?: iconFactory.getConversationDrawable(
- extractAvatarFromRow(this),
- sbn.packageName,
- sbn.uid,
- ranking.channel.isImportantConversation
- )
- return PersonModel(key, sbn.user.identifier, name, drawable, clickRunnable)
- }
-
- private fun NotificationListenerService.Ranking.getIcon(
- iconFactory: ConversationIconFactory,
- sbn: StatusBarNotification
- ): Drawable? =
- conversationShortcutInfo?.let { conversationShortcutInfo ->
- iconFactory.getConversationDrawable(
- conversationShortcutInfo,
- sbn.packageName,
- sbn.uid,
- channel.isImportantConversation
- )
- }
-
- private fun NotificationEntry.extractPersonKey(): PersonKey? {
- // TODO migrate to shortcut id when snoozing is conversation wide
- val type = peopleNotificationIdentifier.getPeopleNotificationType(this)
- return if (type != TYPE_NON_PERSON) key else null
- }
-}
-
-private fun NotificationLockscreenUserManager.registerListener(
- listener: NotificationLockscreenUserManager.UserChangedListener
-): Subscription {
- addUserChangedListener(listener)
- return object : Subscription {
- override fun unsubscribe() {
- removeUserChangedListener(listener)
- }
- }
-}
-
-class PeopleHubManager {
-
- // People currently visible in the notification shade, and so are not in the hub
- private val activePeople = mutableMapOf<PersonKey, PersonModel>()
-
- // People that were once "active" and have been dismissed, and so can be displayed in the hub
- private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
-
- fun migrateActivePerson(key: PersonKey): Boolean {
- activePeople.remove(key)?.let { data ->
- if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
- inactivePeople.removeLast()
- }
- inactivePeople.addFirst(data)
- return true
- }
- return false
- }
-
- fun removeActivePerson(key: PersonKey) {
- activePeople.remove(key)
- }
-
- fun addActivePerson(person: PersonModel): Boolean {
- activePeople[person.key] = person
- return inactivePeople.removeIf { it.key == person.key }
- }
-
- fun getPeopleHubModel(): PeopleHubModel = PeopleHubModel(inactivePeople)
-}
-
-private val ViewGroup.children
- get(): Sequence<View> = sequence {
- for (i in 0 until childCount) {
- yield(getChildAt(i))
- }
- }
-
-private fun ViewGroup.childrenWithId(id: Int): Sequence<View> = children.filter { it.id == id }
-
-fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
- entry.row
- ?.childrenWithId(R.id.expanded)
- ?.mapNotNull { it as? ViewGroup }
- ?.flatMap {
- it.childrenWithId(com.android.internal.R.id.status_bar_latest_event_content)
- }
- ?.mapNotNull {
- it.findViewById<ViewGroup>(com.android.internal.R.id.notification_messaging)
- }
- ?.mapNotNull { messagesView ->
- messagesView.children
- .mapNotNull { it as? MessagingGroup }
- .lastOrNull()
- ?.findViewById<ImageView>(com.android.internal.R.id.message_icon)
- ?.drawable
- }
- ?.firstOrNull()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
deleted file mode 100644
index 55bd77f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.notification.people
-
-import android.content.Context
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
-import android.view.View
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.plugins.ActivityStarter
-import javax.inject.Inject
-
-/** Boundary between the View and PeopleHub, as seen by the View. */
-interface PeopleHubViewAdapter {
- fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription
-}
-
-/** Abstract `View` representation of PeopleHub. */
-interface PeopleHubViewBoundary {
- /** View used for animating the activity launch caused by clicking a person in the hub. */
- val associatedViewForClickAnimation: View
-
- /**
- * [DataListener]s for individual people in the hub.
- *
- * These listeners should be ordered such that the first element will be bound to the most
- * recent person to be added to the hub, and then continuing in descending order. If there are
- * not enough people to satisfy each listener, `null` will be passed instead, indicating that
- * the `View` should render a placeholder.
- */
- val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
-
- /** Sets the visibility of the Hub in the notification shade. */
- fun setVisible(isVisible: Boolean)
-}
-
-/** Creates a [PeopleHubViewModel] given some additional information required from the `View`. */
-interface PeopleHubViewModelFactory {
-
- /**
- * Creates a [PeopleHubViewModel] that, when clicked, starts an activity using an animation
- * involving the given [view].
- */
- fun createWithAssociatedClickView(view: View): PeopleHubViewModel
-}
-
-/**
- * Wraps a [PeopleHubViewBoundary] in a [DataListener], and connects it to the data
- * pipeline.
- *
- * @param dataSource PeopleHub data pipeline.
- */
-@SysUISingleton
-class PeopleHubViewAdapterImpl @Inject constructor(
- private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory>
-) : PeopleHubViewAdapter {
-
- override fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription =
- dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary))
-}
-
-private class PeopleHubDataListenerImpl(
- private val viewBoundary: PeopleHubViewBoundary
-) : DataListener<PeopleHubViewModelFactory> {
-
- override fun onDataChanged(data: PeopleHubViewModelFactory) {
- val viewModel = data.createWithAssociatedClickView(
- viewBoundary.associatedViewForClickAnimation
- )
- viewBoundary.setVisible(viewModel.isVisible)
- val padded = viewModel.people + repeated(null)
- for ((adapter, model) in viewBoundary.personViewAdapters.zip(padded)) {
- adapter.onDataChanged(model)
- }
- }
-}
-
-/**
- * Converts [PeopleHubModel]s into [PeopleHubViewModelFactory]s.
- *
- * This class serves as the glue between the View layer (which depends on
- * [PeopleHubViewBoundary]) and the Data layer (which produces [PeopleHubModel]s).
- */
-@SysUISingleton
-class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
- private val activityStarter: ActivityStarter,
- private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
-) : DataSource<PeopleHubViewModelFactory> {
-
- override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>): Subscription {
- var model: PeopleHubModel? = null
-
- fun updateListener() {
- // don't invoke listener until we've received our first model
- model?.let { model ->
- val factory = PeopleHubViewModelFactoryImpl(model, activityStarter)
- listener.onDataChanged(factory)
- }
- }
- val dataSub = dataSource.registerListener(object : DataListener<PeopleHubModel> {
- override fun onDataChanged(data: PeopleHubModel) {
- model = data
- updateListener()
- }
- })
- return object : Subscription {
- override fun unsubscribe() {
- dataSub.unsubscribe()
- }
- }
- }
-}
-
-private object EmptyViewModelFactory : PeopleHubViewModelFactory {
- override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
- return PeopleHubViewModel(emptySequence(), false)
- }
-}
-
-private class PeopleHubViewModelFactoryImpl(
- private val model: PeopleHubModel,
- private val activityStarter: ActivityStarter
-) : PeopleHubViewModelFactory {
-
- override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
- val personViewModels = model.people.asSequence().map { personModel ->
- val onClick = {
- personModel.clickRunnable.run()
- }
- PersonViewModel(personModel.name, personModel.avatar, onClick)
- }
- return PeopleHubViewModel(personViewModels, model.people.isNotEmpty())
- }
-}
-
-@SysUISingleton
-class PeopleHubSettingChangeDataSourceImpl @Inject constructor(
- @Main private val handler: Handler,
- context: Context
-) : DataSource<Boolean> {
-
- private val settingUri = Settings.Secure.getUriFor(Settings.Secure.PEOPLE_STRIP)
- private val contentResolver = context.contentResolver
-
- override fun registerListener(listener: DataListener<Boolean>): Subscription {
- // Immediately report current value of setting
- updateListener(listener)
- val observer = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean, uri: Uri?, flags: Int) {
- super.onChange(selfChange, uri, flags)
- updateListener(listener)
- }
- }
- contentResolver.registerContentObserver(settingUri, false, observer, UserHandle.USER_ALL)
- return object : Subscription {
- override fun unsubscribe() = contentResolver.unregisterContentObserver(observer)
- }
- }
-
- private fun updateListener(listener: DataListener<Boolean>) {
- val setting = Settings.Secure.getIntForUser(
- contentResolver,
- Settings.Secure.PEOPLE_STRIP,
- 0,
- UserHandle.USER_CURRENT
- )
- listener.onDataChanged(setting != 0)
- }
-}
-
-private fun <T> repeated(value: T): Sequence<T> = sequence {
- while (true) {
- yield(value)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 855390d..8574f87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -25,8 +25,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -68,6 +66,9 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -3247,6 +3248,7 @@
}
@Override
+ @NonNull
public ExpandableViewState createExpandableViewState() {
return new NotificationViewState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 8f73b80..1e09b8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -29,6 +29,7 @@
import android.view.ViewParent;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.Dumpable;
@@ -66,7 +67,7 @@
protected float mContentTransformationAmount;
protected boolean mIsLastChild;
protected int mContentShift;
- private final ExpandableViewState mViewState;
+ @NonNull private final ExpandableViewState mViewState;
private float mContentTranslation;
protected boolean mLastInSection;
protected boolean mFirstInSection;
@@ -610,6 +611,7 @@
public void setActualHeightAnimating(boolean animating) {}
+ @NonNull
protected ExpandableViewState createExpandableViewState() {
return new ExpandableViewState();
}
@@ -642,7 +644,12 @@
return mViewState;
}
- @Nullable public ExpandableViewState getViewState() {
+ /**
+ * Get the {@link ExpandableViewState} associated with the view.
+ *
+ * @return the ExpandableView's view state.
+ */
+ @NonNull public ExpandableViewState getViewState() {
return mViewState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index e43ecf7..49dc655 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -23,6 +23,8 @@
import android.util.IndentingPrintWriter;
import android.view.View;
+import androidx.annotation.NonNull;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -142,6 +144,7 @@
}
@Override
+ @NonNull
public ExpandableViewState createExpandableViewState() {
return new FooterViewState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
deleted file mode 100644
index 6abfee9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2018 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.systemui.statusbar.notification.row;
-
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-import com.android.systemui.statusbar.notification.logging.NotificationCounters;
-import com.android.systemui.util.Compile;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Manager for the notification blocking helper - tracks and helps create the blocking helper
- * affordance.
- */
-public class NotificationBlockingHelperManager {
- /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
- private static final String TAG = "BlockingHelper";
- private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean DEBUG_ALWAYS_SHOW = false;
-
- private final Context mContext;
- private final NotificationGutsManager mNotificationGutsManager;
- private final NotificationEntryManager mNotificationEntryManager;
- private final MetricsLogger mMetricsLogger;
- private final GroupMembershipManager mGroupMembershipManager;
- /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
- private ExpandableNotificationRow mBlockingHelperRow;
- private Set<String> mNonBlockablePkgs;
-
- /**
- * Whether the notification shade/stack is expanded - used to determine blocking helper
- * eligibility.
- */
- private boolean mIsShadeExpanded;
-
- /**
- * Injected constructor. See {@link NotificationsModule}.
- */
- public NotificationBlockingHelperManager(
- Context context,
- NotificationGutsManager notificationGutsManager,
- NotificationEntryManager notificationEntryManager,
- MetricsLogger metricsLogger,
- GroupMembershipManager groupMembershipManager) {
- mContext = context;
- mNotificationGutsManager = notificationGutsManager;
- mNotificationEntryManager = notificationEntryManager;
- mMetricsLogger = metricsLogger;
- mNonBlockablePkgs = new HashSet<>();
- Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
- com.android.internal.R.array.config_nonBlockableNotificationPackages));
- mGroupMembershipManager = groupMembershipManager;
- }
-
- /**
- * Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
- * item, in the current row if user sentiment is negative.
- *
- * @param row row to render the blocking helper in
- * @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
- * blocking helper UI
- * @return whether we're showing a blocking helper in the given notification row
- */
- boolean perhapsShowBlockingHelper(
- ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
- // We only show the blocking helper if:
- // - User sentiment is negative (DEBUG flag can bypass)
- // - The notification shade is fully expanded (guarantees we're not touching a HUN).
- // - The row is blockable (i.e. not non-blockable)
- // - The dismissed row is a valid group (>1 or 0 children from the same channel)
- // or the only child in the group
- final NotificationEntry entry = row.getEntry();
- if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG_ALWAYS_SHOW)
- && mIsShadeExpanded
- && !row.getIsNonblockable()
- && ((!row.isChildInGroup() || mGroupMembershipManager.isOnlyChildInGroup(entry))
- && row.getNumUniqueChannels() <= 1)) {
- // Dismiss any current blocking helper before continuing forward (only one can be shown
- // at a given time).
- dismissCurrentBlockingHelper();
-
- if (DEBUG) {
- Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
- }
-
- // Enable blocking helper on the row before moving forward so everything in the guts is
- // correctly prepped.
- mBlockingHelperRow = row;
- mBlockingHelperRow.setBlockingHelperShowing(true);
-
- // Log triggering of blocking helper by the system. This log line
- // should be emitted before the "display" log line.
- mMetricsLogger.write(
- getLogMaker().setSubtype(MetricsEvent.BLOCKING_HELPER_TRIGGERED_BY_SYSTEM));
-
- // We don't care about the touch origin (x, y) since we're opening guts without any
- // explicit user interaction.
- mNotificationGutsManager.openGuts(
- mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
-
- mMetricsLogger.count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
- return true;
- }
- return false;
- }
-
- /**
- * Dismiss the currently showing blocking helper, if any, through a notification update.
- *
- * @return whether the blocking helper was dismissed
- */
- boolean dismissCurrentBlockingHelper() {
- if (!isBlockingHelperRowNull()) {
- if (DEBUG) {
- Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
- }
- if (!mBlockingHelperRow.isBlockingHelperShowing()) {
- Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
- + "Non-null row is not showing a blocking helper");
- }
-
- mBlockingHelperRow.setBlockingHelperShowing(false);
- if (mBlockingHelperRow.isAttachedToWindow()) {
- mNotificationEntryManager.updateNotifications("dismissCurrentBlockingHelper");
- }
- mBlockingHelperRow = null;
- return true;
- }
- return false;
- }
-
- /**
- * Update the expansion status of the notification shade/stack.
- *
- * @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
- */
- public void setNotificationShadeExpanded(float expandedHeight) {
- mIsShadeExpanded = expandedHeight > 0.0f;
- }
-
- /**
- * Returns whether the given package name is in the list of non-blockable packages.
- */
- public boolean isNonblockable(String packageName, String channelName) {
- return mNonBlockablePkgs.contains(packageName)
- || mNonBlockablePkgs.contains(makeChannelKey(packageName, channelName));
- }
-
- private LogMaker getLogMaker() {
- return mBlockingHelperRow.getEntry().getSbn()
- .getLogMaker()
- .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
- }
-
- // Format must stay in sync with frameworks/base/core/res/res/values/config.xml
- // config_nonBlockableNotificationPackages
- private String makeChannelKey(String pkg, String channel) {
- return pkg + ":" + channel;
- }
-
- @VisibleForTesting
- boolean isBlockingHelperRowNull() {
- return mBlockingHelperRow == null;
- }
-
- @VisibleForTesting
- void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
- mBlockingHelperRow = blockingHelperRowForTest;
- }
-
- @VisibleForTesting
- void setNonBlockablePkgs(String[] pkgsAndChannels) {
- mNonBlockablePkgs = new HashSet<>();
- Collections.addAll(mNonBlockablePkgs, pkgsAndChannels);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 79d883b3..1fb265f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4460,8 +4460,7 @@
}
private void updateVisibility() {
- boolean shouldShow = !mAmbientState.isFullyHidden() || !onKeyguard();
- setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
+ mController.updateVisibility(!mAmbientState.isFullyHidden() || !onKeyguard());
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4526,17 +4525,21 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- void updateEmptyShadeView(boolean visible, boolean notifVisibleInShade) {
+ void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
int oldTextRes = mEmptyShadeView.getTextResource();
- int newTextRes = notifVisibleInShade
+ int newTextRes = areNotificationsHiddenInShade
? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
if (oldTextRes != newTextRes) {
mEmptyShadeView.setText(newTextRes);
}
}
+ public boolean isEmptyShadeViewVisible() {
+ return mEmptyShadeView.isVisible();
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooterView(boolean visible, boolean showDismissView, boolean showHistory) {
if (mFooterView == null) {
@@ -6116,9 +6119,6 @@
}
return row.canViewBeDismissed();
}
- if (v instanceof PeopleHubView) {
- return ((PeopleHubView) v).getCanSwipe();
- }
return false;
}
@@ -6130,9 +6130,6 @@
}
return row.canViewBeCleared();
}
- if (v instanceof PeopleHubView) {
- return ((PeopleHubView) v).getCanSwipe();
- }
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 3f6586c..ec1e620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -87,8 +87,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -179,7 +177,6 @@
private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss;
private NotificationSwipeHelper mSwipeHelper;
- private boolean mShowEmptyShadeView;
@Nullable private Boolean mHistoryEnabled;
private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
@@ -633,7 +630,6 @@
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
CentralSurfaces centralSurfaces,
ScrimController scrimController,
- NotificationGroupManagerLegacy legacyGroupManager,
GroupExpansionManager groupManager,
@SilentHeader SectionHeaderController silentHeaderController,
NotifPipeline notifPipeline,
@@ -677,12 +673,6 @@
mJankMonitor = jankMonitor;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
mGroupExpansionManager = groupManager;
- legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
- @Override
- public void onGroupsChanged() {
- mCentralSurfaces.requestNotificationUpdate("onGroupsChanged");
- }
- });
mSilentHeaderController = silentHeaderController;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
@@ -1182,8 +1172,21 @@
}
/**
- * Update whether we should show the empty shade view (no notifications in the shade).
- * If so, send the update to our view.
+ * Set the visibility of the view, and propagate it to specific children.
+ *
+ * @param visible either the view is visible or not.
+ */
+ public void updateVisibility(boolean visible) {
+ mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+
+ if (mView.getVisibility() == View.VISIBLE) {
+ // Synchronize EmptyShadeView visibility with the parent container.
+ updateShowEmptyShadeView();
+ }
+ }
+
+ /**
+ * Update whether we should show the empty shade view ("no notifications" in the shade).
*
* When in split mode, notifications are always visible regardless of the state of the
* QuickSettings panel. That being the case, empty view is always shown if the other conditions
@@ -1191,18 +1194,31 @@
*/
public void updateShowEmptyShadeView() {
Trace.beginSection("NSSLC.updateShowEmptyShadeView");
- mShowEmptyShadeView = mStatusBarStateController.getCurrentOrUpcomingState() != KEYGUARD
- && !mView.isQsFullScreen()
- && getVisibleNotificationCount() == 0;
- mView.updateEmptyShadeView(
- mShowEmptyShadeView,
- mZenModeController.areNotificationsHiddenInShade());
+ final boolean shouldShow = getVisibleNotificationCount() == 0
+ && !mView.isQsFullScreen()
+ // Hide empty shade view when in transition to Keyguard.
+ // That avoids "No Notifications" to blink when transitioning to AOD.
+ // For more details, see: b/228790482
+ && !isInTransitionToKeyguard();
+
+ mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
+
Trace.endSection();
}
+ /**
+ * @return true if {@link StatusBarStateController} is in transition to the KEYGUARD
+ * and false otherwise.
+ */
+ private boolean isInTransitionToKeyguard() {
+ final int currentState = mStatusBarStateController.getState();
+ final int upcomingState = mStatusBarStateController.getCurrentOrUpcomingState();
+ return (currentState != upcomingState && upcomingState == KEYGUARD);
+ }
+
public boolean isShowingEmptyShadeView() {
- return mShowEmptyShadeView;
+ return mView.isEmptyShadeViewVisible();
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
deleted file mode 100644
index b13e7fb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.notification.stack
-
-import android.annotation.ColorInt
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
-import com.android.systemui.statusbar.notification.people.DataListener
-import com.android.systemui.statusbar.notification.people.PersonViewModel
-import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
-
-class PeopleHubView(context: Context, attrs: AttributeSet) :
- StackScrollerDecorView(context, attrs), SwipeableView {
-
- private lateinit var contents: ViewGroup
- private lateinit var label: TextView
-
- lateinit var personViewAdapters: Sequence<DataListener<PersonViewModel?>>
- private set
-
- override fun onFinishInflate() {
- contents = requireViewById(R.id.people_list)
- label = requireViewById(R.id.header_label)
- personViewAdapters = (0 until contents.childCount)
- .asSequence() // so we can map
- .mapNotNull { idx ->
- // get all our people slots
- (contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl)
- }
- .toList() // cache it
- .asSequence() // but don't reveal it's a list
- super.onFinishInflate()
- setVisible(true /* nowVisible */, false /* animate */)
- }
-
- fun setTextColor(@ColorInt color: Int) = label.setTextColor(color)
-
- override fun findContentView(): View = contents
- override fun findSecondaryView(): View? = null
-
- override fun hasFinishedInitialization(): Boolean = true
-
- override fun createMenu(): NotificationMenuRowPlugin? = null
-
- override fun resetTranslation() {
- translationX = 0f
- }
-
- override fun setTranslation(translation: Float) {
- if (canSwipe) {
- super.setTranslation(translation)
- }
- }
-
- var canSwipe: Boolean = false
- set(value) {
- if (field != value) {
- if (field) {
- resetTranslation()
- }
- field = value
- }
- }
-
- override fun needsClippingToShelf(): Boolean = true
-
- override fun applyContentTransformation(contentAlpha: Float, translationY: Float) {
- super.applyContentTransformation(contentAlpha, translationY)
- for (i in 0 until contents.childCount) {
- val view = contents.getChildAt(i)
- view.alpha = contentAlpha
- view.translationY = translationY
- }
- }
-
- fun setOnHeaderClickListener(listener: OnClickListener) = label.setOnClickListener(listener)
-
- private inner class PersonDataListenerImpl(val avatarView: ImageView) :
- DataListener<PersonViewModel?> {
-
- override fun onDataChanged(data: PersonViewModel?) {
- avatarView.visibility = data?.let { View.VISIBLE } ?: View.GONE
- avatarView.setImageDrawable(data?.icon)
- avatarView.setOnClickListener { data?.onClick?.invoke() }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 6d513d0da..353355b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -120,11 +120,64 @@
updateClipping(algorithmState, ambientState);
updateSpeedBumpState(algorithmState, speedBumpIndex);
updateShelfState(algorithmState, ambientState);
+ updateAlphaState(algorithmState, ambientState);
getNotificationChildrenStates(algorithmState, ambientState);
}
+ private void updateAlphaState(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
+ for (ExpandableView view : algorithmState.visibleChildren) {
+ final ViewState viewState = view.getViewState();
+
+ final boolean isHunGoingToShade = ambientState.isShadeExpanded()
+ && view == ambientState.getTrackedHeadsUpRow();
+
+ if (isHunGoingToShade) {
+ // Keep 100% opacity for heads up notification going to shade.
+ viewState.alpha = 1f;
+ } else if (ambientState.isOnKeyguard()) {
+ // Adjust alpha for wakeup to lockscreen.
+ viewState.alpha = 1f - ambientState.getHideAmount();
+ } else if (ambientState.isExpansionChanging()) {
+ // Adjust alpha for shade open & close.
+ float expansion = ambientState.getExpansionFraction();
+ viewState.alpha = ambientState.isBouncerInTransit()
+ ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion)
+ : ShadeInterpolation.getContentAlpha(expansion);
+ }
+
+ // For EmptyShadeView if on keyguard, we need to control the alpha to create
+ // a nice transition when the user is dragging down the notification panel.
+ if (view instanceof EmptyShadeView && ambientState.isOnKeyguard()) {
+ final float fractionToShade = ambientState.getFractionToShade();
+ viewState.alpha = ShadeInterpolation.getContentAlpha(fractionToShade);
+ }
+
+ NotificationShelf shelf = ambientState.getShelf();
+ if (shelf != null) {
+ final ViewState shelfState = shelf.getViewState();
+
+ // After the shelf has updated its yTranslation, explicitly set alpha=0 for view
+ // below shelf to skip rendering them in the hardware layer. We do not set them
+ // invisible because that runs invalidate & onDraw when these views return onscreen,
+ // which is more expensive.
+ if (shelfState.hidden) {
+ // When the shelf is hidden, it won't clip views, so we don't hide rows
+ return;
+ }
+
+ final float shelfTop = shelfState.yTranslation;
+ final float viewTop = viewState.yTranslation;
+ if (viewTop >= shelfTop) {
+ viewState.alpha = 0;
+ }
+ }
+ }
+ }
+
/**
* How expanded or collapsed notifications are when pulling down the shade.
+ *
* @param ambientState Current ambient state.
* @return 0 when fully collapsed, 1 when expanded.
*/
@@ -208,22 +261,6 @@
}
shelf.updateState(algorithmState, ambientState);
-
- // After the shelf has updated its yTranslation, explicitly set alpha=0 for view below shelf
- // to skip rendering them in the hardware layer. We do not set them invisible because that
- // runs invalidate & onDraw when these views return onscreen, which is more expensive.
- if (shelf.getViewState().hidden) {
- // When the shelf is hidden, it won't clip views, so we don't hide rows
- return;
- }
- final float shelfTop = shelf.getViewState().yTranslation;
-
- for (ExpandableView view : algorithmState.visibleChildren) {
- final float viewTop = view.getViewState().yTranslation;
- if (viewTop >= shelfTop) {
- view.getViewState().alpha = 0;
- }
- }
}
private void updateClipping(StackScrollAlgorithmState algorithmState,
@@ -473,21 +510,6 @@
ExpandableViewState viewState = view.getViewState();
viewState.location = ExpandableViewState.LOCATION_UNKNOWN;
- final boolean isHunGoingToShade = ambientState.isShadeExpanded()
- && view == ambientState.getTrackedHeadsUpRow();
- if (isHunGoingToShade) {
- // Keep 100% opacity for heads up notification going to shade.
- } else if (ambientState.isOnKeyguard()) {
- // Adjust alpha for wakeup to lockscreen.
- viewState.alpha = 1f - ambientState.getHideAmount();
- } else if (ambientState.isExpansionChanging()) {
- // Adjust alpha for shade open & close.
- float expansion = ambientState.getExpansionFraction();
- viewState.alpha = ambientState.isBouncerInTransit()
- ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion)
- : ShadeInterpolation.getContentAlpha(expansion);
- }
-
final float expansionFraction = getExpansionFractionWithoutShelf(
algorithmState, ambientState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 747c4de..52a45d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -55,7 +55,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
@@ -326,12 +325,12 @@
mNotificationPanelViewController.expand(true /* animate */);
mNotificationStackScrollLayoutController.setWillExpand(true);
mHeadsUpManager.unpinAll(true /* userUnpinned */);
- mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
+ mMetricsLogger.count("panel_open", 1);
} else if (!mNotificationPanelViewController.isInSettings()
&& !mNotificationPanelViewController.isExpanding()) {
mNotificationPanelViewController.flingSettings(0 /* velocity */,
- NotificationPanelView.FLING_EXPAND);
- mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
+ NotificationPanelViewController.FLING_EXPAND);
+ mMetricsLogger.count("panel_open_qs", 1);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index ed186ab..8273d57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -33,7 +33,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
@@ -137,11 +136,12 @@
LinearLayout linearLayout,
FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
- Provider<WifiViewModel> wifiViewModelProvider) {
+ Provider<WifiViewModel> wifiViewModelProvider,
+ DarkIconDispatcher darkIconDispatcher) {
super(linearLayout, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_padding);
- mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
+ mDarkIconDispatcher = darkIconDispatcher;
}
@Override
@@ -198,20 +198,24 @@
private final FeatureFlags mFeatureFlags;
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private final Provider<WifiViewModel> mWifiViewModelProvider;
+ private final DarkIconDispatcher mDarkIconDispatcher;
@Inject
public Factory(
FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
- Provider<WifiViewModel> wifiViewModelProvider) {
+ Provider<WifiViewModel> wifiViewModelProvider,
+ DarkIconDispatcher darkIconDispatcher) {
mFeatureFlags = featureFlags;
mStatusBarPipelineFlags = statusBarPipelineFlags;
mWifiViewModelProvider = wifiViewModelProvider;
+ mDarkIconDispatcher = darkIconDispatcher;
}
public DarkIconManager create(LinearLayout group) {
return new DarkIconManager(
- group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider);
+ group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider,
+ mDarkIconDispatcher);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index 7765427..103f3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -46,7 +46,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
/**
* Provides data related to the wifi state.
@@ -118,12 +118,19 @@
}
}
- trySend(WIFI_NETWORK_DEFAULT)
connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
}
- .shareIn(scope, started = SharingStarted.WhileSubscribed())
+ // There will be multiple wifi icons in different places that will frequently
+ // subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
+ // new subscribes will get the latest value immediately upon subscription. Otherwise, the
+ // views could show stale data. See b/244173280.
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = WIFI_NETWORK_DEFAULT
+ )
override val wifiActivity: Flow<WifiActivityModel> =
if (wifiManager == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
new file mode 100644
index 0000000..7baebf4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -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 com.android.systemui.util.kotlin
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.zip
+
+/**
+ * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
+ * Note that the new Flow will not start emitting until it has received two emissions from the
+ * upstream Flow.
+ *
+ * Useful for code that needs to compare the current value to the previous value.
+ */
+fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> {
+ // same as current flow, but with the very first event skipped
+ val nextEvents = drop(1)
+ // zip current flow and nextEvents; transform will receive a pair of old and new value. This
+ // works because zip will suppress emissions until both flows have emitted something; since in
+ // this case both flows are emitting at the same rate, but the current flow just has one extra
+ // thing emitted at the start, the effect is that zip will cache the most recent value while
+ // waiting for the next emission from nextEvents.
+ return zip(nextEvents, transform)
+}
+
+/**
+ * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
+ * [initialValue] will be used as the "old" value for the first emission.
+ *
+ * Useful for code that needs to compare the current value to the previous value.
+ */
+fun <T, R> Flow<T>.pairwiseBy(
+ initialValue: T,
+ transform: suspend (previousValue: T, newValue: T) -> R,
+): Flow<R> =
+ onStart { emit(initialValue) }.pairwiseBy(transform)
+
+/**
+ * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new
+ * Flow will not start emitting until it has received two emissions from the upstream Flow.
+ *
+ * Useful for code that needs to compare the current value to the previous value.
+ */
+fun <T> Flow<T>.pairwise(): Flow<WithPrev<T>> = pairwiseBy(::WithPrev)
+
+/**
+ * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue]
+ * will be used as the "old" value for the first emission.
+ *
+ * Useful for code that needs to compare the current value to the previous value.
+ */
+fun <T> Flow<T>.pairwise(initialValue: T): Flow<WithPrev<T>> = pairwiseBy(initialValue, ::WithPrev)
+
+/** Holds a [newValue] emitted from a [Flow], along with the [previousValue] emitted value. */
+data class WithPrev<T>(val previousValue: T, val newValue: T)
+
+/**
+ * Returns a new [Flow] that combines the [Set] changes between each emission from [this] using
+ * [transform].
+ */
+fun <T, R> Flow<Set<T>>.setChangesBy(
+ transform: suspend (removed: Set<T>, added: Set<T>) -> R,
+): Flow<R> = onStart { emit(emptySet()) }.distinctUntilChanged()
+ .pairwiseBy { old: Set<T>, new: Set<T> ->
+ // If an element was present in the old set, but not the new one, then it was removed
+ val removed = old - new
+ // If an element is present in the new set, but on the old one, then it was added
+ val added = new - old
+ transform(removed, added)
+ }
+
+/** Returns a new [Flow] that produces the [Set] changes between each emission from [this]. */
+fun <T> Flow<Set<T>>.setChanges(): Flow<SetChanges<T>> = setChangesBy(::SetChanges)
+
+/** Contains the difference in elements between two [Set]s. */
+data class SetChanges<T>(
+ /** Elements that are present in the first [Set] but not in the second. */
+ val removed: Set<T>,
+ /** Elements that are present in the second [Set] but not in the first. */
+ val added: Set<T>,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OWNERS b/packages/SystemUI/src/com/android/systemui/volume/OWNERS
new file mode 100644
index 0000000..e627d61
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/OWNERS
@@ -0,0 +1,4 @@
+asc@google.com # send reviews here
+
+juliacr@google.com
+tsuji@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 13c3df3..c7ba518 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -48,7 +48,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -248,6 +247,7 @@
private final ConfigurationController mConfigurationController;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
+ private final VolumePanelFactory mVolumePanelFactory;
private final ActivityStarter mActivityStarter;
private boolean mShowing;
@@ -279,6 +279,7 @@
DeviceProvisionedController deviceProvisionedController,
ConfigurationController configurationController,
MediaOutputDialogFactory mediaOutputDialogFactory,
+ VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
InteractionJankMonitor interactionJankMonitor) {
mContext =
@@ -290,6 +291,7 @@
mDeviceProvisionedController = deviceProvisionedController;
mConfigurationController = configurationController;
mMediaOutputDialogFactory = mediaOutputDialogFactory;
+ mVolumePanelFactory = volumePanelFactory;
mActivityStarter = activityStarter;
mShowActiveStreamOnly = showActiveStreamOnly();
mHasSeenODICaptionsTooltip =
@@ -1045,10 +1047,9 @@
if (mSettingsIcon != null) {
mSettingsIcon.setOnClickListener(v -> {
Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
- Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
dismissH(DISMISS_REASON_SETTINGS_CLICKED);
mMediaOutputDialogFactory.dismiss();
- mActivityStarter.startActivity(intent, true /* dismissShade */);
+ mVolumePanelFactory.create(true /* aboveStatusBar */, null);
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
new file mode 100644
index 0000000..2c74fb9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
@@ -0,0 +1,299 @@
+/*
+ * 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.systemui.volume;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.provider.SettingsSlicesContract;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.lifecycle.LiveData;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.slice.Slice;
+import androidx.slice.SliceMetadata;
+import androidx.slice.widget.EventInfo;
+import androidx.slice.widget.SliceLiveData;
+
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.MediaOutputConstants;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Visual presentation of the volume panel dialog.
+ */
+public class VolumePanelDialog extends SystemUIDialog implements LifecycleOwner {
+ private static final String TAG = "VolumePanelDialog";
+
+ private static final int DURATION_SLICE_BINDING_TIMEOUT_MS = 200;
+ private static final int DEFAULT_SLICE_SIZE = 4;
+
+ private RecyclerView mVolumePanelSlices;
+ private VolumePanelSlicesAdapter mVolumePanelSlicesAdapter;
+ private final LifecycleRegistry mLifecycleRegistry;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Map<Uri, LiveData<Slice>> mSliceLiveData = new LinkedHashMap<>();
+ private final HashSet<Uri> mLoadedSlices = new HashSet<>();
+ private boolean mSlicesReadyToLoad;
+ private LocalBluetoothProfileManager mProfileManager;
+
+ public VolumePanelDialog(Context context, boolean aboveStatusBar) {
+ super(context);
+ mLifecycleRegistry = new LifecycleRegistry(this);
+ if (!aboveStatusBar) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(TAG, "onCreate");
+
+ View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.volume_panel_dialog,
+ null);
+ final Window window = getWindow();
+ window.setContentView(dialogView);
+
+ Button doneButton = dialogView.findViewById(R.id.done_button);
+ doneButton.setOnClickListener(v -> dismiss());
+ Button settingsButton = dialogView.findViewById(R.id.settings_button);
+ settingsButton.setOnClickListener(v -> {
+ getContext().startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS).addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK));
+ dismiss();
+ });
+
+ LocalBluetoothManager localBluetoothManager = LocalBluetoothManager.getInstance(
+ getContext(), null);
+ if (localBluetoothManager != null) {
+ mProfileManager = localBluetoothManager.getProfileManager();
+ }
+
+ mVolumePanelSlices = dialogView.findViewById(R.id.volume_panel_parent_layout);
+ mVolumePanelSlices.setLayoutManager(new LinearLayoutManager(getContext()));
+
+ loadAllSlices();
+
+ mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
+ }
+
+ private void loadAllSlices() {
+ mSliceLiveData.clear();
+ mLoadedSlices.clear();
+ final List<Uri> sliceUris = getSlices();
+
+ for (Uri uri : sliceUris) {
+ final LiveData<Slice> sliceLiveData = SliceLiveData.fromUri(getContext(), uri,
+ (int type, Throwable source) -> {
+ if (!removeSliceLiveData(uri)) {
+ mLoadedSlices.add(uri);
+ }
+ });
+
+ // Add slice first to make it in order. Will remove it later if there's an error.
+ mSliceLiveData.put(uri, sliceLiveData);
+
+ sliceLiveData.observe(this, slice -> {
+ if (mLoadedSlices.contains(uri)) {
+ return;
+ }
+ Log.d(TAG, "received slice: " + (slice == null ? null : slice.getUri()));
+ final SliceMetadata metadata = SliceMetadata.from(getContext(), slice);
+ if (slice == null || metadata.isErrorSlice()) {
+ if (!removeSliceLiveData(uri)) {
+ mLoadedSlices.add(uri);
+ }
+ } else if (metadata.getLoadingState() == SliceMetadata.LOADED_ALL) {
+ mLoadedSlices.add(uri);
+ } else {
+ mHandler.postDelayed(() -> {
+ mLoadedSlices.add(uri);
+ setupAdapterWhenReady();
+ }, DURATION_SLICE_BINDING_TIMEOUT_MS);
+ }
+
+ setupAdapterWhenReady();
+ });
+ }
+ }
+
+ private void setupAdapterWhenReady() {
+ if (mLoadedSlices.size() == mSliceLiveData.size() && !mSlicesReadyToLoad) {
+ mSlicesReadyToLoad = true;
+ mVolumePanelSlicesAdapter = new VolumePanelSlicesAdapter(this, mSliceLiveData);
+ mVolumePanelSlicesAdapter.setOnSliceActionListener((eventInfo, sliceItem) -> {
+ if (eventInfo.actionType == EventInfo.ACTION_TYPE_SLIDER) {
+ return;
+ }
+ this.dismiss();
+ });
+ if (mSliceLiveData.size() < DEFAULT_SLICE_SIZE) {
+ mVolumePanelSlices.setMinimumHeight(0);
+ }
+ mVolumePanelSlices.setAdapter(mVolumePanelSlicesAdapter);
+ }
+ }
+
+ private boolean removeSliceLiveData(Uri uri) {
+ boolean removed = false;
+ // Keeps observe media output slice
+ if (!uri.equals(MEDIA_OUTPUT_INDICATOR_SLICE_URI)) {
+ Log.d(TAG, "remove uri: " + uri);
+ removed = mSliceLiveData.remove(uri) != null;
+ if (mVolumePanelSlicesAdapter != null) {
+ mVolumePanelSlicesAdapter.updateDataSet(new ArrayList<>(mSliceLiveData.values()));
+ }
+ }
+ return removed;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "onStart");
+ mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
+ mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.d(TAG, "onStop");
+ mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
+ }
+
+ private List<Uri> getSlices() {
+ final List<Uri> uris = new ArrayList<>();
+ uris.add(REMOTE_MEDIA_SLICE_URI);
+ uris.add(VOLUME_MEDIA_URI);
+ Uri controlUri = getExtraControlUri();
+ if (controlUri != null) {
+ Log.d(TAG, "add extra control slice");
+ uris.add(controlUri);
+ }
+ uris.add(MEDIA_OUTPUT_INDICATOR_SLICE_URI);
+ uris.add(VOLUME_CALL_URI);
+ uris.add(VOLUME_RINGER_URI);
+ uris.add(VOLUME_ALARM_URI);
+ return uris;
+ }
+
+ private static final String SETTINGS_SLICE_AUTHORITY = "com.android.settings.slices";
+ private static final Uri REMOTE_MEDIA_SLICE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SETTINGS_SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath(MediaOutputConstants.KEY_REMOTE_MEDIA)
+ .build();
+ private static final Uri VOLUME_MEDIA_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SETTINGS_SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath("media_volume")
+ .build();
+ private static final Uri MEDIA_OUTPUT_INDICATOR_SLICE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SETTINGS_SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_INTENT)
+ .appendPath("media_output_indicator")
+ .build();
+ private static final Uri VOLUME_CALL_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SETTINGS_SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath("call_volume")
+ .build();
+ private static final Uri VOLUME_RINGER_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SETTINGS_SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath("ring_volume")
+ .build();
+ private static final Uri VOLUME_ALARM_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SETTINGS_SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath("alarm_volume")
+ .build();
+
+ private Uri getExtraControlUri() {
+ Uri controlUri = null;
+ final BluetoothDevice bluetoothDevice = findActiveDevice();
+ if (bluetoothDevice != null) {
+ // The control slice width = dialog width - horizontal padding of two sides
+ final int dialogWidth =
+ getWindow().getWindowManager().getCurrentWindowMetrics().getBounds().width();
+ final int controlSliceWidth = dialogWidth
+ - getContext().getResources().getDimensionPixelSize(
+ R.dimen.volume_panel_slice_horizontal_padding) * 2;
+ final String uri = BluetoothUtils.getControlUriMetaData(bluetoothDevice);
+ if (!TextUtils.isEmpty(uri)) {
+ try {
+ controlUri = Uri.parse(uri + controlSliceWidth);
+ } catch (NullPointerException exception) {
+ Log.d(TAG, "unable to parse extra control uri");
+ controlUri = null;
+ }
+ }
+ }
+ return controlUri;
+ }
+
+ private BluetoothDevice findActiveDevice() {
+ if (mProfileManager != null) {
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null) {
+ return a2dpProfile.getActiveDevice();
+ }
+ }
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycleRegistry;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt
new file mode 100644
index 0000000..f11d5d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.systemui.volume
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.provider.Settings
+import android.text.TextUtils
+import android.util.Log
+import javax.inject.Inject
+
+private const val TAG = "VolumePanelDialogReceiver"
+private const val LAUNCH_ACTION = "com.android.systemui.action.LAUNCH_VOLUME_PANEL_DIALOG"
+private const val DISMISS_ACTION = "com.android.systemui.action.DISMISS_VOLUME_PANEL_DIALOG"
+
+/**
+ * BroadcastReceiver for handling volume panel dialog intent
+ */
+class VolumePanelDialogReceiver @Inject constructor(
+ private val volumePanelFactory: VolumePanelFactory
+) : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ Log.d(TAG, "onReceive intent" + intent.action)
+ if (TextUtils.equals(LAUNCH_ACTION, intent.action) ||
+ TextUtils.equals(Settings.Panel.ACTION_VOLUME, intent.action)) {
+ volumePanelFactory.create(true, null)
+ } else if (TextUtils.equals(DISMISS_ACTION, intent.action)) {
+ volumePanelFactory.dismiss()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
new file mode 100644
index 0000000..c2fafbf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.systemui.volume
+
+import android.content.Context
+import android.util.Log
+import android.view.View
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+private const val TAG = "VolumePanelFactory"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+/**
+ * Factory to create [VolumePanelDialog] objects. This is the dialog that allows the user to adjust
+ * multiple streams with sliders.
+ */
+@SysUISingleton
+class VolumePanelFactory @Inject constructor(
+ private val context: Context,
+ private val dialogLaunchAnimator: DialogLaunchAnimator
+) {
+ companion object {
+ var volumePanelDialog: VolumePanelDialog? = null
+ }
+
+ /** Creates a [VolumePanelDialog]. The dialog will be animated from [view] if it is not null. */
+ fun create(aboveStatusBar: Boolean, view: View? = null) {
+ if (volumePanelDialog?.isShowing == true) {
+ return
+ }
+
+ val dialog = VolumePanelDialog(context, aboveStatusBar)
+ volumePanelDialog = dialog
+
+ // Show the dialog.
+ if (view != null) {
+ dialogLaunchAnimator.showFromView(dialog, view, animateBackgroundBoundsChange = true)
+ } else {
+ dialog.show()
+ }
+ }
+
+ /** Dismiss [VolumePanelDialog] if exist. */
+ fun dismiss() {
+ if (DEBUG) {
+ Log.d(TAG, "dismiss dialog")
+ }
+ volumePanelDialog?.dismiss()
+ volumePanelDialog = null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelSlicesAdapter.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelSlicesAdapter.java
new file mode 100644
index 0000000..2371402
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelSlicesAdapter.java
@@ -0,0 +1,137 @@
+/*
+ * 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.systemui.volume;
+
+import static android.app.slice.Slice.HINT_ERROR;
+import static android.app.slice.SliceItem.FORMAT_SLICE;
+
+import android.content.Context;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.widget.SliceView;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * RecyclerView adapter for Slices in Settings Panels.
+ */
+public class VolumePanelSlicesAdapter extends
+ RecyclerView.Adapter<VolumePanelSlicesAdapter.SliceRowViewHolder> {
+
+ private final List<LiveData<Slice>> mSliceLiveData;
+ private final LifecycleOwner mLifecycleOwner;
+ private SliceView.OnSliceActionListener mOnSliceActionListener;
+
+ public VolumePanelSlicesAdapter(LifecycleOwner lifecycleOwner,
+ Map<Uri, LiveData<Slice>> sliceLiveData) {
+ mLifecycleOwner = lifecycleOwner;
+ mSliceLiveData = new ArrayList<>(sliceLiveData.values());
+ }
+
+ @NonNull
+ @Override
+ public SliceRowViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
+ final Context context = viewGroup.getContext();
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ View view = inflater.inflate(R.layout.volume_panel_slice_slider_row, viewGroup, false);
+ return new SliceRowViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull SliceRowViewHolder sliceRowViewHolder, int position) {
+ sliceRowViewHolder.onBind(mSliceLiveData.get(position), position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSliceLiveData.size();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return position;
+ }
+
+ void setOnSliceActionListener(SliceView.OnSliceActionListener listener) {
+ mOnSliceActionListener = listener;
+ }
+
+ void updateDataSet(ArrayList<LiveData<Slice>> list) {
+ mSliceLiveData.clear();
+ mSliceLiveData.addAll(list);
+ notifyDataSetChanged();
+ }
+
+ /**
+ * ViewHolder for binding Slices to SliceViews.
+ */
+ public class SliceRowViewHolder extends RecyclerView.ViewHolder {
+
+ private final SliceView mSliceView;
+
+ public SliceRowViewHolder(View view) {
+ super(view);
+ mSliceView = view.findViewById(R.id.slice_view);
+ mSliceView.setMode(SliceView.MODE_LARGE);
+ mSliceView.setShowTitleItems(true);
+ mSliceView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mSliceView.setOnSliceActionListener(mOnSliceActionListener);
+ }
+
+ /**
+ * Called when the view is displayed.
+ */
+ public void onBind(LiveData<Slice> sliceLiveData, int position) {
+ sliceLiveData.observe(mLifecycleOwner, mSliceView);
+
+ // Do not show the divider above media devices switcher slice per request
+ final Slice slice = sliceLiveData.getValue();
+
+ // Hides slice which reports with error hint or not contain any slice sub-item.
+ if (slice == null || !isValidSlice(slice)) {
+ mSliceView.setVisibility(View.GONE);
+ } else {
+ mSliceView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private boolean isValidSlice(Slice slice) {
+ if (slice.getHints().contains(HINT_ERROR)) {
+ return false;
+ }
+ for (SliceItem item : slice.getItems()) {
+ if (item.getFormat().equals(FORMAT_SLICE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index f3855bd..c5792b9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -30,6 +30,7 @@
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogImpl;
+import com.android.systemui.volume.VolumePanelFactory;
import dagger.Binds;
import dagger.Module;
@@ -52,6 +53,7 @@
DeviceProvisionedController deviceProvisionedController,
ConfigurationController configurationController,
MediaOutputDialogFactory mediaOutputDialogFactory,
+ VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
InteractionJankMonitor interactionJankMonitor) {
VolumeDialogImpl impl = new VolumeDialogImpl(
@@ -61,6 +63,7 @@
deviceProvisionedController,
configurationController,
mediaOutputDialogFactory,
+ volumePanelFactory,
activityStarter,
interactionJankMonitor);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 199048e..ce96741 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -62,7 +62,6 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -103,7 +102,6 @@
private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationLockscreenUserManager mNotifUserManager;
- private final NotificationGroupManagerLegacy mNotificationGroupManager;
private final CommonNotifCollection mCommonNotifCollection;
private final NotifPipeline mNotifPipeline;
private final Executor mSysuiMainExecutor;
@@ -130,7 +128,6 @@
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
- NotificationGroupManagerLegacy groupManager,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -148,7 +145,6 @@
interruptionStateProvider,
zenModeController,
notifUserManager,
- groupManager,
notifCollection,
notifPipeline,
sysUiState,
@@ -171,7 +167,6 @@
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
- NotificationGroupManagerLegacy groupManager,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -185,7 +180,6 @@
mVisibilityProvider = visibilityProvider;
mNotificationInterruptStateProvider = interruptionStateProvider;
mNotifUserManager = notifUserManager;
- mNotificationGroupManager = groupManager;
mCommonNotifCollection = notifCollection;
mNotifPipeline = notifPipeline;
mSysuiMainExecutor = sysuiMainExecutor;
@@ -331,16 +325,6 @@
}
@Override
- public void removeNotificationEntry(String key) {
- sysuiMainExecutor.execute(() -> {
- final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
- if (entry != null) {
- mNotificationGroupManager.onEntryRemoved(entry);
- }
- });
- }
-
- @Override
public void updateNotificationBubbleButton(String key) {
sysuiMainExecutor.execute(() -> {
final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
@@ -351,16 +335,6 @@
}
@Override
- public void updateNotificationSuppression(String key) {
- sysuiMainExecutor.execute(() -> {
- final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
- if (entry != null) {
- mNotificationGroupManager.updateSuppression(entry);
- }
- });
- }
-
- @Override
public void onStackExpandChanged(boolean shouldExpand) {
sysuiMainExecutor.execute(() -> {
sysUiState.setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
@@ -532,7 +506,10 @@
REASON_GROUP_SUMMARY_CANCELED);
}
} else {
- mNotificationGroupManager.onEntryRemoved(entry);
+ for (NotifCallback cb : mCallbacks) {
+ cb.removeNotification(entry, getDismissedByUserStats(entry, true),
+ REASON_GROUP_SUMMARY_CANCELED);
+ }
}
}, mSysuiMainExecutor);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index aecec9d..d68e8bd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -387,6 +387,33 @@
}
@Test
+ public void onResume_sideFpsHintShouldBeShown_sideFpsHintShown() {
+ setupGetSecurityView();
+ setupConditionsToEnableSideFpsHint();
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onResume(0);
+
+ verify(mSidefpsController).show();
+ verify(mSidefpsController, never()).hide();
+ }
+
+ @Test
+ public void onResume_sideFpsHintShouldNotBeShown_sideFpsHintHidden() {
+ setupGetSecurityView();
+ setupConditionsToEnableSideFpsHint();
+ setSideFpsHintEnabledFromResources(false);
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onResume(0);
+
+ verify(mSidefpsController).hide();
+ verify(mSidefpsController, never()).show();
+ }
+
+ @Test
public void showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() {
// GIVEN the current security method is SimPin
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
new file mode 100644
index 0000000..6b1ef38
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
@@ -0,0 +1,179 @@
+package com.android.systemui
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flag
+import com.android.systemui.flags.FlagListenable
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.UnreleasedFlag
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.TestCoroutineDispatcher
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ChooserSelectorTest : SysuiTestCase() {
+
+ private val flagListener = kotlinArgumentCaptor<FlagListenable.Listener>()
+
+ private val testDispatcher = TestCoroutineDispatcher()
+ private val testScope = CoroutineScope(testDispatcher)
+
+ private lateinit var chooserSelector: ChooserSelector
+
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockPackageManager: PackageManager
+ @Mock private lateinit var mockResources: Resources
+ @Mock private lateinit var mockFeatureFlags: FeatureFlags
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(mockContext.packageManager).thenReturn(mockPackageManager)
+ `when`(mockContext.resources).thenReturn(mockResources)
+ `when`(mockResources.getString(anyInt())).thenReturn(
+ ComponentName("TestPackage", "TestClass").flattenToString())
+
+ chooserSelector = ChooserSelector(mockContext, mockFeatureFlags, testScope, testDispatcher)
+ }
+
+ @After
+ fun tearDown() {
+ testDispatcher.cleanupTestCoroutines()
+ }
+
+ @Test
+ fun initialize_registersFlagListenerUntilScopeCancelled() {
+ // Arrange
+
+ // Act
+ chooserSelector.start()
+
+ // Assert
+ verify(mockFeatureFlags).addListener(
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ verify(mockFeatureFlags, never()).removeListener(any())
+
+ // Act
+ testScope.cancel()
+
+ // Assert
+ verify(mockFeatureFlags).removeListener(eq(flagListener.value))
+ }
+
+ @Test
+ fun initialize_enablesUnbundledChooser_whenFlagEnabled() {
+ // Arrange
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+
+ // Act
+ chooserSelector.start()
+
+ // Assert
+ verify(mockPackageManager).setComponentEnabledSetting(
+ eq(ComponentName("TestPackage", "TestClass")),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
+ anyInt())
+ }
+
+ @Test
+ fun initialize_disablesUnbundledChooser_whenFlagDisabled() {
+ // Arrange
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+
+ // Act
+ chooserSelector.start()
+
+ // Assert
+ verify(mockPackageManager).setComponentEnabledSetting(
+ eq(ComponentName("TestPackage", "TestClass")),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
+ anyInt())
+ }
+
+ @Test
+ fun enablesUnbundledChooser_whenFlagBecomesEnabled() {
+ // Arrange
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ chooserSelector.start()
+ verify(mockFeatureFlags).addListener(
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ verify(mockPackageManager, never()).setComponentEnabledSetting(
+ any(), eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED), anyInt())
+
+ // Act
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
+
+ // Assert
+ verify(mockPackageManager).setComponentEnabledSetting(
+ eq(ComponentName("TestPackage", "TestClass")),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
+ anyInt())
+ }
+
+ @Test
+ fun disablesUnbundledChooser_whenFlagBecomesDisabled() {
+ // Arrange
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ chooserSelector.start()
+ verify(mockFeatureFlags).addListener(
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ verify(mockPackageManager, never()).setComponentEnabledSetting(
+ any(), eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED), anyInt())
+
+ // Act
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
+
+ // Assert
+ verify(mockPackageManager).setComponentEnabledSetting(
+ eq(ComponentName("TestPackage", "TestClass")),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
+ anyInt())
+ }
+
+ @Test
+ fun doesNothing_whenAnotherFlagChanges() {
+ // Arrange
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ chooserSelector.start()
+ verify(mockFeatureFlags).addListener(
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ clearInvocations(mockPackageManager)
+
+ // Act
+ `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id + 1))
+
+ // Assert
+ verifyZeroInteractions(mockPackageManager)
+ }
+
+ private class TestFlagEvent(override val flagId: Int) : FlagListenable.FlagEvent {
+ override fun requestNoRestart() {}
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 64a7986..df10dfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -16,7 +16,6 @@
import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -36,6 +35,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -49,7 +49,6 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.graphics.Insets;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -60,7 +59,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.RotationUtils;
import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
@@ -80,6 +78,8 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.decor.CornerDecorProvider;
+import com.android.systemui.decor.CutoutDecorProviderFactory;
+import com.android.systemui.decor.CutoutDecorProviderImpl;
import com.android.systemui.decor.DecorProvider;
import com.android.systemui.decor.DecorProviderFactory;
import com.android.systemui.decor.FaceScanningOverlayProviderImpl;
@@ -157,6 +157,9 @@
@Mock
private DisplayInfo mDisplayInfo;
private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener;
+ @Mock
+ private CutoutDecorProviderFactory mCutoutFactory;
+ private List<DecorProvider> mMockCutoutList;
@Before
public void setup() {
@@ -206,6 +209,11 @@
DisplayCutout.BOUNDS_POSITION_RIGHT,
R.layout.privacy_dot_bottom_right));
+ // Default no cutout
+ mMockCutoutList = new ArrayList<>();
+ doAnswer(it -> !(mMockCutoutList.isEmpty())).when(mCutoutFactory).getHasProviders();
+ doReturn(mMockCutoutList).when(mCutoutFactory).getProviders();
+
mFaceScanningDecorProvider = spy(new FaceScanningOverlayProviderImpl(
BOUNDS_POSITION_TOP,
mAuthController,
@@ -239,6 +247,11 @@
super.updateOverlayWindowVisibilityIfViewExists(view);
mExecutor.runAllReady();
}
+
+ @Override
+ protected CutoutDecorProviderFactory getCutoutFactory() {
+ return ScreenDecorationsTest.this.mCutoutFactory;
+ }
});
mScreenDecorations.mDisplayInfo = mDisplayInfo;
doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
@@ -429,11 +442,9 @@
public void testNoRounding_NoCutout_NoPrivacyDot_NoFaceScanning() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
- // no cutout
- doReturn(null).when(mScreenDecorations).getCutout();
+ // no cutout (default)
mScreenDecorations.start();
// No views added.
@@ -448,11 +459,9 @@
public void testNoRounding_NoCutout_PrivacyDot_NoFaceScanning() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
- // no cutout
- doReturn(null).when(mScreenDecorations).getCutout();
+ // no cutout (default)
mScreenDecorations.start();
@@ -484,11 +493,9 @@
public void testRounding_NoCutout_NoPrivacyDot_NoFaceScanning() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
- // no cutout
- doReturn(null).when(mScreenDecorations).getCutout();
+ // no cutout (default)
mScreenDecorations.start();
@@ -516,11 +523,9 @@
public void testRounding_NoCutout_PrivacyDot_NoFaceScanning() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
- // no cutout
- doReturn(null).when(mScreenDecorations).getCutout();
+ // no cutout (default)
mScreenDecorations.start();
@@ -555,10 +560,9 @@
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
- // no cutout
- doReturn(null).when(mScreenDecorations).getCutout();
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
+
+ // no cutout (default)
mScreenDecorations.start();
// Size of corner view should same as rounded_corner_radius{_top|_bottom}
@@ -574,11 +578,9 @@
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
- // no cutout
- doReturn(null).when(mScreenDecorations).getCutout();
+ // no cutout (default)
mScreenDecorations.start();
View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
@@ -611,13 +613,10 @@
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
/* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// left cutout
- final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
@@ -647,13 +646,10 @@
public void testNoRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for top cutout.
@@ -671,13 +667,10 @@
public void testNoRounding_CutoutShortEdge_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for top cutout.
@@ -706,13 +699,10 @@
public void testNoRounding_CutoutLongEdge_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// left cutout
- final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for left cutout.
@@ -734,13 +724,10 @@
public void testNoRounding_CutoutLongEdge_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// left cutout
- final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for left cutout.
@@ -762,13 +749,10 @@
public void testRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
@@ -791,13 +775,10 @@
public void testRounding_CutoutShortEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
@@ -823,13 +804,10 @@
public void testRounding_CutoutLongEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// left cutout
- final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for rounded corner and left cutout.
@@ -842,13 +820,10 @@
public void testRounding_CutoutLongEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// left cutout
- final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for rounded corner, left cutout, and privacy.
@@ -863,13 +838,11 @@
public void testRounding_CutoutShortAndLongEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top and left cutout
- final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
@@ -883,13 +856,11 @@
public void testRounding_CutoutShortAndLongEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// top and left cutout
- final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
@@ -905,21 +876,16 @@
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// Set to short edge cutout(top).
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE);
// Switch to long edge cutout(left).
- final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.onConfigurationChanged(new Configuration());
verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE);
@@ -929,13 +895,10 @@
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// Set to short edge cutout(top).
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
@@ -943,9 +906,7 @@
verify(mDotViewController, times(1)).setShowingListener(null);
// Switch to long edge cutout(left).
- final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.onConfigurationChanged(new Configuration());
verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
@@ -973,20 +934,16 @@
public void testDelayedCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
- // top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ // No cutout (default)
mScreenDecorations.start();
verifyOverlaysExistAndAdded(false, false, false, false, null);
- when(mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout))
- .thenReturn(true);
+ // top cutout
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
+
mScreenDecorations.onConfigurationChanged(new Configuration());
// Only top windows should be added.
@@ -997,13 +954,9 @@
public void testDelayedCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
- // top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ // no cutout (default)
mScreenDecorations.start();
// Both top and bottom windows should be added with INVISIBLE because of only privacy dot,
@@ -1015,9 +968,9 @@
verify(mDotViewController, times(1)).setShowingListener(
mScreenDecorations.mPrivacyDotShowingListener);
- when(mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout))
- .thenReturn(true);
+ // top cutout
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
+
mScreenDecorations.onConfigurationChanged(new Configuration());
// Both top and bottom windows should be added with VISIBLE because of privacy dot and
@@ -1043,8 +996,7 @@
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
/* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning*/);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/);
mDisplayInfo.rotation = Surface.ROTATION_0;
mScreenDecorations.start();
@@ -1058,8 +1010,7 @@
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
/* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning*/);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/);
mDisplayInfo.rotation = Surface.ROTATION_270;
mScreenDecorations.onConfigurationChanged(null);
@@ -1072,8 +1023,7 @@
public void testOnlyRoundedCornerRadiusTop() {
setupResources(0 /* radius */, 10 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
@@ -1094,8 +1044,7 @@
public void testOnlyRoundedCornerRadiusBottom() {
setupResources(0 /* radius */, 0 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
@@ -1166,13 +1115,10 @@
public void testSupportHwcLayer_SwitchFrom_NotSupport() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// should only inflate mOverlays when the hwc doesn't support screen decoration
@@ -1195,16 +1141,13 @@
public void testNotSupportHwcLayer_SwitchFrom_Support() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// should only inflate hwc layer when the hwc supports screen decoration
@@ -1234,16 +1177,13 @@
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
/* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- true /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer
@@ -1277,11 +1217,9 @@
public void testAutoShowHideOverlayWindowWhenNoRoundedAndNoCutout() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
- true /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
- // no cutout
- doReturn(null).when(mScreenDecorations).getCutout();
+ // no cutout (default)
mScreenDecorations.start();
// Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer
@@ -1315,16 +1253,13 @@
public void testHwcLayer_noPrivacyDot_noFaceScanning() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
@@ -1337,16 +1272,13 @@
public void testHwcLayer_PrivacyDot_FaceScanning() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- true /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
@@ -1364,16 +1296,13 @@
public void testOnDisplayChanged_hwcLayer() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
@@ -1390,18 +1319,16 @@
public void testOnDisplayChanged_nonHwcLayer() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top cutout
- final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
- doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
- .when(mScreenDecorations).getCutout();
+ mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
- final ScreenDecorations.DisplayCutoutView cutoutView =
- mScreenDecorations.mCutoutViews[BOUNDS_POSITION_TOP];
+ final ScreenDecorations.DisplayCutoutView cutoutView = (ScreenDecorations.DisplayCutoutView)
+ mScreenDecorations.getOverlayView(R.id.display_cutout);
+ assertNotNull(cutoutView);
spyOn(cutoutView);
doReturn(mDisplay).when(cutoutView).getDisplay();
@@ -1414,8 +1341,7 @@
public void testHasSameProvidersWithNullOverlays() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
@@ -1433,8 +1359,7 @@
public void testHasSameProvidersWithPrivacyDots() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
- false /* faceScanning */);
+ 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
@@ -1471,7 +1396,7 @@
private void setupResources(int radius, int radiusTop, int radiusBottom,
@Nullable Drawable roundedTopDrawable, @Nullable Drawable roundedBottomDrawable,
- int roundedPadding, boolean fillCutout, boolean privacyDot, boolean faceScanning) {
+ int roundedPadding, boolean privacyDot, boolean faceScanning) {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.array.config_displayUniqueIdArray,
new String[]{});
@@ -1511,8 +1436,6 @@
}
mContext.getOrCreateTestableResources().addOverride(
R.dimen.rounded_corner_content_padding, roundedPadding);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
mPrivacyDecorProviders = new ArrayList<>();
if (privacyDot) {
@@ -1531,19 +1454,4 @@
when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(faceScanning);
}
-
- private DisplayCutout getDisplayCutoutForRotation(Insets safeInsets, Rect[] cutoutBounds) {
- final int rotation = mContext.getDisplay().getRotation();
- final Insets insets = RotationUtils.rotateInsets(safeInsets, rotation);
- final Rect[] sorted = new Rect[BOUNDS_POSITION_LENGTH];
- for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- final int rotatedPos = ScreenDecorations.getBoundPositionFromRotation(i, rotation);
- if (cutoutBounds[i] != null) {
- RotationUtils.rotateBounds(cutoutBounds[i], new Rect(0, 0, 100, 200), rotation);
- }
- sorted[rotatedPos] = cutoutBounds[i];
- }
- return new DisplayCutout(insets, sorted[BOUNDS_POSITION_LEFT], sorted[BOUNDS_POSITION_TOP],
- sorted[BOUNDS_POSITION_RIGHT], sorted[BOUNDS_POSITION_BOTTOM]);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e0d1f7a..b18b0ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -20,6 +20,8 @@
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -157,13 +159,15 @@
@Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Captor
- ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
+ private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
@Captor
- ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor;
+ private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor;
@Captor
- ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor;
+ private ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor;
@Captor
- ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+ private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+ @Captor
+ private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefullnessObserverCaptor;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -242,7 +246,9 @@
mFaceAuthenticatorsRegisteredCaptor.capture());
when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
+ verify(mWakefulnessLifecycle).addObserver(mWakefullnessObserverCaptor.capture());
mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps);
mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps);
@@ -759,16 +765,37 @@
}
@Test
- public void testForwardsDozeEvent() throws RemoteException {
+ public void testForwardsDozeEvents() throws RemoteException {
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
mAuthController.setBiometicContextListener(mContextListener);
- mStatusBarStateListenerCaptor.getValue().onDozingChanged(false);
mStatusBarStateListenerCaptor.getValue().onDozingChanged(true);
+ mStatusBarStateListenerCaptor.getValue().onDozingChanged(false);
InOrder order = inOrder(mContextListener);
- // invoked twice since the initial state is false
- order.verify(mContextListener, times(2)).onDozeChanged(eq(false));
- order.verify(mContextListener).onDozeChanged(eq(true));
+ order.verify(mContextListener, times(2)).onDozeChanged(eq(true), eq(true));
+ order.verify(mContextListener).onDozeChanged(eq(false), eq(true));
+ order.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testForwardsWakeEvents() throws RemoteException {
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
+ mAuthController.setBiometicContextListener(mContextListener);
+
+ mWakefullnessObserverCaptor.getValue().onStartedGoingToSleep();
+ mWakefullnessObserverCaptor.getValue().onFinishedGoingToSleep();
+ mWakefullnessObserverCaptor.getValue().onStartedWakingUp();
+ mWakefullnessObserverCaptor.getValue().onFinishedWakingUp();
+ mWakefullnessObserverCaptor.getValue().onPostFinishedWakingUp();
+
+ InOrder order = inOrder(mContextListener);
+ order.verify(mContextListener).onDozeChanged(eq(false), eq(true));
+ order.verify(mContextListener).onDozeChanged(eq(false), eq(false));
+ order.verify(mContextListener).onDozeChanged(eq(false), eq(true));
+ order.verifyNoMoreInteractions();
}
// Helpers
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 3ac28c8..2af0557 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -107,11 +107,11 @@
reset(rippleView)
captor.value.onThemeChanged()
- verify(rippleView).setColor(ArgumentMatchers.anyInt())
+ verify(rippleView).setColor(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
reset(rippleView)
captor.value.onUiModeChanged()
- verify(rippleView).setColor(ArgumentMatchers.anyInt())
+ verify(rippleView).setColor(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
new file mode 100644
index 0000000..1040ec4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
@@ -0,0 +1,200 @@
+/*
+ * 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.systemui.decor
+
+import android.graphics.Insets
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableResources
+import android.util.RotationUtils
+import android.util.Size
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH
+import android.view.DisplayInfo
+import android.view.Surface
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class CutoutDecorProviderFactoryTest : SysuiTestCase() {
+
+ @Mock private lateinit var display: Display
+ private var testableRes: TestableResources? = null
+ private lateinit var factory: CutoutDecorProviderFactory
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableRes = mContext.orCreateTestableResources
+ factory = CutoutDecorProviderFactory(testableRes!!.resources, display)
+ }
+
+ private fun setupFillCutout(fillCutout: Boolean) {
+ testableRes!!.addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout
+ )
+ }
+
+ private fun setupDisplayInfo(
+ displayCutout: DisplayCutout? = null,
+ @Surface.Rotation rotation: Int = Surface.ROTATION_0,
+ displayId: Int = -1
+ ) {
+ doAnswer {
+ it.getArgument<DisplayInfo>(0).let { info ->
+ info.displayCutout = displayCutout
+ info.rotation = rotation
+ info.displayId = displayId
+ }
+ true
+ }.`when`(display).getDisplayInfo(any<DisplayInfo>())
+ }
+
+ private fun getCutout(
+ safeInsets: Insets,
+ cutoutBounds: Array<Rect?>,
+ @Surface.Rotation rotation: Int = Surface.ROTATION_0,
+ cutoutParentSizeForRotate: Size = Size(100, 200)
+ ): DisplayCutout {
+ val insets = RotationUtils.rotateInsets(safeInsets, rotation)
+ val sorted = arrayOfNulls<Rect>(BOUNDS_POSITION_LENGTH)
+ for (pos in 0 until BOUNDS_POSITION_LENGTH) {
+ val rotatedPos = (pos - rotation + BOUNDS_POSITION_LENGTH) % BOUNDS_POSITION_LENGTH
+ if (cutoutBounds[pos] != null) {
+ RotationUtils.rotateBounds(
+ cutoutBounds[pos],
+ cutoutParentSizeForRotate.width,
+ cutoutParentSizeForRotate.height,
+ rotation
+ )
+ }
+ sorted[rotatedPos] = cutoutBounds[pos]
+ }
+ return DisplayCutout(
+ insets,
+ sorted[DisplayCutout.BOUNDS_POSITION_LEFT],
+ sorted[DisplayCutout.BOUNDS_POSITION_TOP],
+ sorted[DisplayCutout.BOUNDS_POSITION_RIGHT],
+ sorted[DisplayCutout.BOUNDS_POSITION_BOTTOM]
+ )
+ }
+
+ @Test
+ fun testGetNothingIfNoCutout() {
+ setupFillCutout(false)
+
+ Assert.assertFalse(factory.hasProviders)
+ Assert.assertEquals(0, factory.providers.size)
+ }
+
+ @Test
+ fun testGetTopCutoutProvider() {
+ setupFillCutout(true)
+ setupDisplayInfo(
+ getCutout(
+ safeInsets = Insets.of(0, 1, 0, 0),
+ cutoutBounds = arrayOf(null, Rect(9, 0, 10, 1), null, null)
+ )
+ )
+
+ Assert.assertTrue(factory.hasProviders)
+
+ val providers = factory.providers
+ Assert.assertEquals(1, providers.size)
+ Assert.assertEquals(1, providers[0].numOfAlignedBound)
+ Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, providers[0].alignedBounds[0])
+ }
+
+ @Test
+ fun testGetBottomCutoutProviderOnLandscape() {
+ setupFillCutout(true)
+ setupDisplayInfo(
+ getCutout(
+ safeInsets = Insets.of(0, 0, 0, 1),
+ cutoutBounds = arrayOf(null, null, null, Rect(45, 199, 55, 200)),
+ rotation = Surface.ROTATION_90
+ ),
+ Surface.ROTATION_90
+ )
+
+ Assert.assertTrue(factory.hasProviders)
+
+ val providers = factory.providers
+ Assert.assertEquals(1, providers.size)
+ Assert.assertEquals(1, providers[0].numOfAlignedBound)
+ Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_BOTTOM, providers[0].alignedBounds[0])
+ }
+
+ @Test
+ fun testGetLeftCutoutProviderOnSeascape() {
+ setupFillCutout(true)
+ setupDisplayInfo(
+ getCutout(
+ safeInsets = Insets.of(1, 0, 0, 0),
+ cutoutBounds = arrayOf(Rect(0, 20, 1, 40), null, null, null),
+ rotation = Surface.ROTATION_270
+ ),
+ Surface.ROTATION_270
+ )
+
+ Assert.assertTrue(factory.hasProviders)
+
+ val providers = factory.providers
+ Assert.assertEquals(1, providers.size)
+ Assert.assertEquals(1, providers[0].numOfAlignedBound)
+ Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_LEFT, providers[0].alignedBounds[0])
+ }
+
+ @Test
+ fun testGetTopRightCutoutProviderOnReverse() {
+ setupFillCutout(true)
+ setupDisplayInfo(
+ getCutout(
+ safeInsets = Insets.of(0, 1, 1, 0),
+ cutoutBounds = arrayOf(
+ null,
+ Rect(9, 0, 10, 1),
+ Rect(99, 40, 100, 60),
+ null
+ ),
+ rotation = Surface.ROTATION_180
+ ),
+ Surface.ROTATION_180
+ )
+
+ Assert.assertTrue(factory.hasProviders)
+
+ val providers = factory.providers
+ Assert.assertEquals(2, providers.size)
+ Assert.assertEquals(1, providers[0].numOfAlignedBound)
+ Assert.assertEquals(1, providers[1].numOfAlignedBound)
+ providers.sortedBy { it.alignedBounds[0] }.let {
+ Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, it[0].alignedBounds[0])
+ Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_RIGHT, it[1].alignedBounds[0])
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
new file mode 100644
index 0000000..bc94440
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.systemui.dreams.complication;
+
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.media.dream.MediaDreamComplication;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DreamMediaEntryComplicationTest extends SysuiTestCase {
+ @Mock
+ private View mView;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private MediaDreamComplication mMediaComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures clicking media entry chip adds/removes media complication.
+ */
+ @Test
+ public void testClick() {
+ final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
+ new DreamMediaEntryComplication.DreamMediaEntryViewController(
+ mView,
+ mDreamOverlayStateController,
+ mMediaComplication);
+
+ final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mView).setOnClickListener(clickListenerCaptor.capture());
+
+ clickListenerCaptor.getValue().onClick(mView);
+ verify(mView).setSelected(true);
+ verify(mDreamOverlayStateController).addComplication(mMediaComplication);
+ clickListenerCaptor.getValue().onClick(mView);
+ verify(mView).setSelected(false);
+ verify(mDreamOverlayStateController).removeComplication(mMediaComplication);
+ }
+
+ /**
+ * Ensures media complication is removed when the view is detached.
+ */
+ @Test
+ public void testOnViewDetached() {
+ final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
+ new DreamMediaEntryComplication.DreamMediaEntryViewController(
+ mView,
+ mDreamOverlayStateController,
+ mMediaComplication);
+
+ viewController.onViewDetached();
+ verify(mView).setSelected(false);
+ verify(mDreamOverlayStateController).removeComplication(mMediaComplication);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
index 7d54758..fa8f88a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
@@ -43,7 +43,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Arrays;
+import java.util.Collections;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -61,9 +61,6 @@
private SmartSpaceComplication mComplication;
@Mock
- private ComplicationViewModel mComplicationViewModel;
-
- @Mock
private View mBcSmartspaceView;
@Before
@@ -125,12 +122,12 @@
// Test
final SmartspaceTarget target = Mockito.mock(SmartspaceTarget.class);
- listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
+ listenerCaptor.getValue().onSmartspaceTargetsUpdated(Collections.singletonList(target));
verify(mDreamOverlayStateController).addComplication(eq(mComplication));
}
@Test
- public void testOverlayActive_targetsEmpty_removesComplication() {
+ public void testOverlayActive_targetsEmpty_addsComplication() {
final SmartSpaceComplication.Registrant registrant = getRegistrant();
registrant.start();
@@ -145,13 +142,9 @@
ArgumentCaptor.forClass(BcSmartspaceDataPlugin.SmartspaceTargetListener.class);
verify(mSmartspaceController).addListener(listenerCaptor.capture());
- final SmartspaceTarget target = Mockito.mock(SmartspaceTarget.class);
- listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
- verify(mDreamOverlayStateController).addComplication(eq(mComplication));
-
// Test
- listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList());
- verify(mDreamOverlayStateController).removeComplication(eq(mComplication));
+ listenerCaptor.getValue().onSmartspaceTargetsUpdated(Collections.emptyList());
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
}
@Test
@@ -170,8 +163,7 @@
ArgumentCaptor.forClass(BcSmartspaceDataPlugin.SmartspaceTargetListener.class);
verify(mSmartspaceController).addListener(listenerCaptor.capture());
- final SmartspaceTarget target = Mockito.mock(SmartspaceTarget.class);
- listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
+ listenerCaptor.getValue().onSmartspaceTargetsUpdated(Collections.emptyList());
verify(mDreamOverlayStateController).addComplication(eq(mComplication));
// Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index ff579a1..318f2bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -41,7 +41,7 @@
* specified. If not, an exception is thrown.
*/
@Test
- fun throwsIfUnspecifiedFlagIsAccessed() {
+ fun accessingUnspecifiedFlags_throwsException() {
val flags: FeatureFlags = FakeFeatureFlags()
try {
assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
@@ -88,7 +88,7 @@
}
@Test
- fun specifiedFlagsReturnCorrectValues() {
+ fun specifiedFlags_returnCorrectValues() {
val flags = FakeFeatureFlags()
flags.set(unreleasedFlag, false)
flags.set(releasedFlag, false)
@@ -114,4 +114,125 @@
assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
assertThat(flags.getString(resourceStringFlag)).isEqualTo("Android")
}
+
+ @Test
+ fun listenerForBooleanFlag_calledOnlyWhenFlagChanged() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(unreleasedFlag, listener)
+
+ flags.set(unreleasedFlag, true)
+ flags.set(unreleasedFlag, true)
+ flags.set(unreleasedFlag, false)
+ flags.set(unreleasedFlag, false)
+
+ listener.verifyInOrder(unreleasedFlag.id, unreleasedFlag.id)
+ }
+
+ @Test
+ fun listenerForStringFlag_calledOnlyWhenFlagChanged() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(stringFlag, listener)
+
+ flags.set(stringFlag, "Test")
+ flags.set(stringFlag, "Test")
+
+ listener.verifyInOrder(stringFlag.id)
+ }
+
+ @Test
+ fun listenerForBooleanFlag_notCalledAfterRemoved() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(unreleasedFlag, listener)
+ flags.set(unreleasedFlag, true)
+ flags.removeListener(listener)
+ flags.set(unreleasedFlag, false)
+
+ listener.verifyInOrder(unreleasedFlag.id)
+ }
+
+ @Test
+ fun listenerForStringFlag_notCalledAfterRemoved() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+
+ flags.addListener(stringFlag, listener)
+ flags.set(stringFlag, "Test")
+ flags.removeListener(listener)
+ flags.set(stringFlag, "Other")
+
+ listener.verifyInOrder(stringFlag.id)
+ }
+
+ @Test
+ fun listenerForMultipleFlags_calledWhenFlagsChange() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(unreleasedFlag, listener)
+ flags.addListener(releasedFlag, listener)
+
+ flags.set(releasedFlag, true)
+ flags.set(unreleasedFlag, true)
+
+ listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
+ }
+
+ @Test
+ fun listenerForMultipleFlags_notCalledAfterRemoved() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+
+ flags.addListener(unreleasedFlag, listener)
+ flags.addListener(releasedFlag, listener)
+ flags.set(releasedFlag, true)
+ flags.set(unreleasedFlag, true)
+ flags.removeListener(listener)
+ flags.set(releasedFlag, false)
+ flags.set(unreleasedFlag, false)
+
+ listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
+ }
+
+ @Test
+ fun multipleListenersForSingleFlag_allAreCalledWhenChanged() {
+ val flags = FakeFeatureFlags()
+ val listener1 = VerifyingListener()
+ val listener2 = VerifyingListener()
+ flags.addListener(releasedFlag, listener1)
+ flags.addListener(releasedFlag, listener2)
+
+ flags.set(releasedFlag, true)
+
+ listener1.verifyInOrder(releasedFlag.id)
+ listener2.verifyInOrder(releasedFlag.id)
+ }
+
+ @Test
+ fun multipleListenersForSingleFlag_removedListenerNotCalledAfterRemoval() {
+ val flags = FakeFeatureFlags()
+ val listener1 = VerifyingListener()
+ val listener2 = VerifyingListener()
+ flags.addListener(releasedFlag, listener1)
+ flags.addListener(releasedFlag, listener2)
+
+ flags.set(releasedFlag, true)
+ flags.removeListener(listener2)
+ flags.set(releasedFlag, false)
+
+ listener1.verifyInOrder(releasedFlag.id, releasedFlag.id)
+ listener2.verifyInOrder(releasedFlag.id)
+ }
+
+ class VerifyingListener : FlagListenable.Listener {
+ var flagEventIds = mutableListOf<Int>()
+ override fun onFlagChanged(event: FlagListenable.FlagEvent) {
+ flagEventIds.add(event.flagId)
+ }
+
+ fun verifyInOrder(vararg eventIds: Int) {
+ assertThat(flagEventIds).containsExactlyElementsIn(eventIds.asList())
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 6e89bb9..21c018a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -48,7 +48,6 @@
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.logging.KeyguardViewMediatorLogger;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -107,7 +106,6 @@
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
private @Mock DreamOverlayStateController mDreamOverlayStateController;
private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
- private @Mock KeyguardViewMediatorLogger mLogger;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -264,8 +262,7 @@
mInteractionJankMonitor,
mDreamOverlayStateController,
mNotificationShadeWindowControllerLazy,
- () -> mActivityLaunchAnimator,
- mLogger);
+ () -> mActivityLaunchAnimator);
mViewMediator.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 7b12eb7..56aff3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -41,18 +41,6 @@
}
@Test
- fun log_shouldSaveLogToBufferWithException() {
- val exception = createTestException("Some exception test message", "SomeExceptionTestClass")
- buffer.log("Test", LogLevel.INFO, "Some test message", exception)
-
- val dumpedString = dumpBuffer()
-
- assertThat(dumpedString).contains("Some test message")
- assertThat(dumpedString).contains("Some exception test message")
- assertThat(dumpedString).contains("SomeExceptionTestClass")
- }
-
- @Test
fun log_shouldRotateIfLogBufferIsFull() {
buffer.log("Test", LogLevel.INFO, "This should be rotated")
buffer.log("Test", LogLevel.INFO, "New test message")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 1785022..bef4695 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -1051,6 +1051,17 @@
}
@Test
+ fun bindDeviceWithNullName() {
+ val fallbackString = context.getResources().getString(R.string.media_seamless_other_device)
+ player.attachPlayer(viewHolder)
+ val state = mediaData.copy(device = device.copy(name = null))
+ player.bindPlayer(state, PACKAGE)
+ assertThat(seamless.isEnabled()).isTrue()
+ assertThat(seamlessText.getText()).isEqualTo(fallbackString)
+ assertThat(seamless.contentDescription).isEqualTo(fallbackString)
+ }
+
+ @Test
fun bindDeviceResumptionPlayer() {
player.attachPlayer(viewHolder)
val state = mediaData.copy(resumption = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index d1ed8e9..f9c7d2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -31,7 +31,6 @@
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
@@ -108,6 +107,7 @@
private val clock = FakeSystemClock()
@Mock private lateinit var tunerService: TunerService
@Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable>
+ @Captor lateinit var callbackCaptor: ArgumentCaptor<(String, PlaybackState) -> Unit>
private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
@@ -974,7 +974,6 @@
fun testPlaybackStateChange_keyExists_callsListener() {
// Notification has been added
addNotificationAndLoad()
- val callbackCaptor = argumentCaptor<(String, PlaybackState) -> Unit>()
verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
// Callback gets an updated state
@@ -992,7 +991,6 @@
@Test
fun testPlaybackStateChange_keyDoesNotExist_doesNothing() {
val state = PlaybackState.Builder().build()
- val callbackCaptor = argumentCaptor<(String, PlaybackState) -> Unit>()
verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
// No media added with this key
@@ -1013,7 +1011,6 @@
// And then get a state update
val state = PlaybackState.Builder().build()
- val callbackCaptor = argumentCaptor<(String, PlaybackState) -> Unit>()
verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
// Then no changes are made
@@ -1022,6 +1019,83 @@
anyBoolean())
}
+ @Test
+ fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ val state = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PAUSED, 0L, 1f)
+ .build()
+ whenever(controller.playbackState).thenReturn(state)
+
+ addNotificationAndLoad()
+ verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
+ callbackCaptor.value.invoke(KEY, state)
+
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY),
+ capture(mediaDataCaptor), eq(true), eq(0), eq(false))
+ assertThat(mediaDataCaptor.value.isPlaying).isFalse()
+ assertThat(mediaDataCaptor.value.semanticActions).isNotNull()
+ }
+
+ @Test
+ fun testPlaybackState_PauseStateAfterAddingResumption_keyExists_callsListener() {
+ val desc = MediaDescription.Builder().run {
+ setTitle(SESSION_TITLE)
+ build()
+ }
+ val state = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PAUSED, 0L, 1f)
+ .setActions(PlaybackState.ACTION_PLAY_PAUSE)
+ .build()
+
+ // Add resumption controls in order to have semantic actions.
+ // To make sure that they are not null after changing state.
+ mediaDataManager.addResumptionControls(
+ USER_ID,
+ desc,
+ Runnable {},
+ session.sessionToken,
+ APP_NAME,
+ pendingIntent,
+ PACKAGE_NAME
+ )
+ backgroundExecutor.runAllReady()
+ foregroundExecutor.runAllReady()
+
+ verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
+ callbackCaptor.value.invoke(PACKAGE_NAME, state)
+
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(PACKAGE_NAME),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.isPlaying).isFalse()
+ assertThat(mediaDataCaptor.value.semanticActions).isNotNull()
+ }
+
+ @Test
+ fun testPlaybackStateNull_Pause_keyExists_callsListener() {
+ whenever(controller.playbackState).thenReturn(null)
+ val state = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PAUSED, 0L, 1f)
+ .setActions(PlaybackState.ACTION_PLAY_PAUSE)
+ .build()
+
+ addNotificationAndLoad()
+ verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
+ callbackCaptor.value.invoke(KEY, state)
+
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY),
+ capture(mediaDataCaptor), eq(true), eq(0), eq(false))
+ assertThat(mediaDataCaptor.value.isPlaying).isFalse()
+ assertThat(mediaDataCaptor.value.semanticActions).isNull()
+ }
+
/**
* Helper function to add a media notification and capture the resulting MediaData
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index ee10426..121c894 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -59,8 +59,8 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private const val KEY = "TEST_KEY"
private const val KEY_OLD = "TEST_KEY_OLD"
@@ -402,9 +402,10 @@
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
- // THEN the device is disabled
+ // THEN the device is disabled and name is set to null
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
+ assertThat(data.name).isNull()
}
@Test
@@ -421,9 +422,10 @@
deviceCallback.onSelectedDeviceStateChanged(device, 1)
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
- // THEN the device is disabled
+ // THEN the device is disabled and name is set to null
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
+ assertThat(data.name).isNull()
}
@Test
@@ -440,9 +442,24 @@
deviceCallback.onDeviceListUpdate(mutableListOf(device))
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
- // THEN the device is disabled
+ // THEN the device is disabled and name is set to null
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
+ assertThat(data.name).isNull()
+ }
+
+ @Test
+ fun mr2ReturnsRouteWithNullName_useLocalDeviceName() {
+ // GIVEN that MR2Manager returns a routing session that does not have a name
+ whenever(route.name).thenReturn(null)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is enabled and uses the current connected device name
+ val data = captureDeviceData(KEY)
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ assertThat(data.enabled).isTrue()
}
@Test
@@ -647,12 +664,14 @@
override fun onPlaybackStopped(reason: Int, broadcastId: Int) {}
override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {}
override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {}
- override fun onBroadcastMetadataChanged(broadcastId: Int,
- metadata: BluetoothLeBroadcastMetadata) {}
+ override fun onBroadcastMetadataChanged(
+ broadcastId: Int,
+ metadata: BluetoothLeBroadcastMetadata
+ ) {}
}
bluetoothLeBroadcast.registerCallback(fakeFgExecutor, callback)
- return callback;
+ return callback
}
fun setupLeAudioConfiguration(isLeAudio: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 6173692..9be201e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -224,6 +224,14 @@
}
@Test
+ public void refresh_inDragging_directSetRefreshingToFalse() {
+ when(mMediaOutputBaseAdapter.isDragging()).thenReturn(true);
+ mMediaOutputBaseDialogImpl.refresh();
+
+ assertThat(mMediaOutputController.isRefreshing()).isFalse();
+ }
+
+ @Test
public void refresh_notInDragging_verifyUpdateAdapter() {
when(mMediaOutputBaseAdapter.getCurrentActivePosition()).thenReturn(-1);
when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index c101b9f..2e864dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -18,6 +18,7 @@
import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
+import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
@@ -30,6 +31,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.DreamMediaEntryComplication;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaData;
import com.android.systemui.media.MediaDataManager;
@@ -51,7 +53,7 @@
DreamOverlayStateController mDreamOverlayStateController;
@Mock
- MediaDreamComplication mComplication;
+ DreamMediaEntryComplication mMediaEntryComplication;
@Mock
FeatureFlags mFeatureFlags;
@@ -72,7 +74,7 @@
@Test
public void testComplicationAddition() {
final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
- mDreamOverlayStateController, mComplication, mFeatureFlags);
+ mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
sentinel.start();
@@ -85,14 +87,16 @@
when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
/* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
- verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ verify(mDreamOverlayStateController).addComplication(eq(mMediaEntryComplication));
+ verify(mDreamOverlayStateController, never()).addComplication(
+ not(eq(mMediaEntryComplication)));
listener.onMediaDataRemoved(mKey);
verify(mDreamOverlayStateController, never()).removeComplication(any());
when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
listener.onMediaDataRemoved(mKey);
- verify(mDreamOverlayStateController).removeComplication(eq(mComplication));
+ verify(mDreamOverlayStateController).removeComplication(eq(mMediaEntryComplication));
}
@Test
@@ -100,7 +104,7 @@
when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(false);
final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
- mDreamOverlayStateController, mComplication, mFeatureFlags);
+ mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
sentinel.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 1061e3c..fa47a74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -35,7 +35,9 @@
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -48,11 +50,12 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -78,6 +81,10 @@
private lateinit var viewUtil: ViewUtil
@Mock
private lateinit var commandQueue: CommandQueue
+ @Mock
+ private lateinit var falsingManager: FalsingManager
+ @Mock
+ private lateinit var falsingCollector: FalsingCollector
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var fakeClock: FakeSystemClock
@@ -115,7 +122,9 @@
accessibilityManager,
configurationController,
powerManager,
- senderUiEventLogger
+ senderUiEventLogger,
+ falsingManager,
+ falsingCollector
)
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
@@ -421,6 +430,38 @@
}
@Test
+ fun transferToReceiverSucceeded_withUndoRunnable_falseTap_callbackNotRun() {
+ whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
+ var undoCallbackCalled = false
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
+ override fun onUndoTriggered() {
+ undoCallbackCalled = true
+ }
+ }
+
+ controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ getChipView().getUndoButton().performClick()
+
+ assertThat(undoCallbackCalled).isFalse()
+ }
+
+ @Test
+ fun transferToReceiverSucceeded_withUndoRunnable_realTap_callbackRun() {
+ whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
+ var undoCallbackCalled = false
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
+ override fun onUndoTriggered() {
+ undoCallbackCalled = true
+ }
+ }
+
+ controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ getChipView().getUndoButton().performClick()
+
+ assertThat(undoCallbackCalled).isTrue()
+ }
+
+ @Test
fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index 48fbd35..073c23c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -23,6 +23,7 @@
import android.graphics.Rect
import android.hardware.HardwareBuffer
import android.os.Bundle
+import android.os.UserHandle
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
@@ -97,7 +98,7 @@
policy.setManagedProfile(USER_ID, false)
policy.setDisplayContentInfo(
policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+ DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
val processor = RequestProcessor(imageCapture, policy, flags, scope)
@@ -120,7 +121,7 @@
// Indicate that the primary content belongs to a manged profile
policy.setManagedProfile(USER_ID, true)
policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+ DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
val processor = RequestProcessor(imageCapture, policy, flags, scope)
@@ -160,7 +161,7 @@
policy.setManagedProfile(USER_ID, false)
policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+ DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
val processedRequest = processor.process(request)
@@ -183,7 +184,7 @@
// Indicate that the primary content belongs to a manged profile
policy.setManagedProfile(USER_ID, true)
policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+ DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
val processedRequest = processor.process(request)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
new file mode 100644
index 0000000..17396b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
@@ -0,0 +1,227 @@
+/*
+ * 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.systemui.screenshot
+
+import android.app.ActivityTaskManager.RootTaskInfo
+import android.app.IActivityTaskManager
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.content.ComponentName
+import android.content.Context
+import android.graphics.Rect
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// The following values are chosen to be distinct from commonly seen real values
+private const val DISPLAY_ID = 100
+private const val PRIMARY_USER = 2000
+private const val MANAGED_PROFILE_USER = 3000
+
+@RunWith(AndroidTestingRunner::class)
+class ScreenshotPolicyImplTest : SysuiTestCase() {
+
+ @Test
+ fun testToDisplayContentInfo() {
+ assertThat(fullScreenWorkProfileTask.toDisplayContentInfo())
+ .isEqualTo(
+ DisplayContentInfo(
+ ComponentName(
+ "com.google.android.apps.nbu.files",
+ "com.google.android.apps.nbu.files.home.HomeActivity"
+ ),
+ Rect(0, 0, 1080, 2400),
+ UserHandle.of(MANAGED_PROFILE_USER),
+ 65))
+ }
+
+ @Test
+ fun findPrimaryContent_ignoresPipTask() = runBlocking {
+ val policy = fakeTasksPolicyImpl(
+ mContext,
+ shadeExpanded = false,
+ tasks = listOf(
+ pipTask,
+ fullScreenWorkProfileTask,
+ launcherTask,
+ emptyTask)
+ )
+
+ val info = policy.findPrimaryContent(DISPLAY_ID)
+ assertThat(info).isEqualTo(fullScreenWorkProfileTask.toDisplayContentInfo())
+ }
+
+ @Test
+ fun findPrimaryContent_shadeExpanded_ignoresTopTask() = runBlocking {
+ val policy = fakeTasksPolicyImpl(
+ mContext,
+ shadeExpanded = true,
+ tasks = listOf(
+ fullScreenWorkProfileTask,
+ launcherTask,
+ emptyTask)
+ )
+
+ val info = policy.findPrimaryContent(DISPLAY_ID)
+ assertThat(info).isEqualTo(policy.systemUiContent)
+ }
+
+ @Test
+ fun findPrimaryContent_emptyTaskList() = runBlocking {
+ val policy = fakeTasksPolicyImpl(
+ mContext,
+ shadeExpanded = false,
+ tasks = listOf()
+ )
+
+ val info = policy.findPrimaryContent(DISPLAY_ID)
+ assertThat(info).isEqualTo(policy.systemUiContent)
+ }
+
+ @Test
+ fun findPrimaryContent_workProfileNotOnTop() = runBlocking {
+ val policy = fakeTasksPolicyImpl(
+ mContext,
+ shadeExpanded = false,
+ tasks = listOf(
+ launcherTask,
+ fullScreenWorkProfileTask,
+ emptyTask)
+ )
+
+ val info = policy.findPrimaryContent(DISPLAY_ID)
+ assertThat(info).isEqualTo(launcherTask.toDisplayContentInfo())
+ }
+
+ private fun fakeTasksPolicyImpl(
+ context: Context,
+ shadeExpanded: Boolean,
+ tasks: List<RootTaskInfo>
+ ): ScreenshotPolicyImpl {
+ val userManager = mock<UserManager>()
+ val atmService = mock<IActivityTaskManager>()
+ val dispatcher = Dispatchers.Unconfined
+
+ return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher) {
+ override suspend fun isManagedProfile(userId: Int) = (userId == MANAGED_PROFILE_USER)
+ override suspend fun getAllRootTaskInfosOnDisplay(displayId: Int) = tasks
+ override suspend fun isNotificationShadeExpanded() = shadeExpanded
+ }
+ }
+
+ private val pipTask = RootTaskInfo().apply {
+ configuration.windowConfiguration.apply {
+ windowingMode = WINDOWING_MODE_PINNED
+ bounds = Rect(628, 1885, 1038, 2295)
+ activityType = ACTIVITY_TYPE_STANDARD
+ }
+ displayId = DISPLAY_ID
+ userId = PRIMARY_USER
+ taskId = 66
+ visible = true
+ isVisible = true
+ isRunning = true
+ numActivities = 1
+ topActivity = ComponentName(
+ "com.google.android.youtube",
+ "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity"
+ )
+ childTaskIds = intArrayOf(66)
+ childTaskNames = arrayOf("com.google.android.youtube/" +
+ "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity")
+ childTaskUserIds = intArrayOf(0)
+ childTaskBounds = arrayOf(Rect(628, 1885, 1038, 2295))
+ }
+
+ private val fullScreenWorkProfileTask = RootTaskInfo().apply {
+ configuration.windowConfiguration.apply {
+ windowingMode = WINDOWING_MODE_FULLSCREEN
+ bounds = Rect(0, 0, 1080, 2400)
+ activityType = ACTIVITY_TYPE_STANDARD
+ }
+ displayId = DISPLAY_ID
+ userId = MANAGED_PROFILE_USER
+ taskId = 65
+ visible = true
+ isVisible = true
+ isRunning = true
+ numActivities = 1
+ topActivity = ComponentName(
+ "com.google.android.apps.nbu.files",
+ "com.google.android.apps.nbu.files.home.HomeActivity"
+ )
+ childTaskIds = intArrayOf(65)
+ childTaskNames = arrayOf("com.google.android.apps.nbu.files/" +
+ "com.google.android.apps.nbu.files.home.HomeActivity")
+ childTaskUserIds = intArrayOf(MANAGED_PROFILE_USER)
+ childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400))
+ }
+
+ private val launcherTask = RootTaskInfo().apply {
+ configuration.windowConfiguration.apply {
+ windowingMode = WINDOWING_MODE_FULLSCREEN
+ bounds = Rect(0, 0, 1080, 2400)
+ activityType = ACTIVITY_TYPE_HOME
+ }
+ displayId = DISPLAY_ID
+ taskId = 1
+ userId = PRIMARY_USER
+ visible = true
+ isVisible = true
+ isRunning = true
+ numActivities = 1
+ topActivity = ComponentName(
+ "com.google.android.apps.nexuslauncher",
+ "com.google.android.apps.nexuslauncher.NexusLauncherActivity",
+ )
+ childTaskIds = intArrayOf(1)
+ childTaskNames = arrayOf("com.google.android.apps.nexuslauncher/" +
+ "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
+ childTaskUserIds = intArrayOf(0)
+ childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400))
+ }
+
+ private val emptyTask = RootTaskInfo().apply {
+ configuration.windowConfiguration.apply {
+ windowingMode = WINDOWING_MODE_FULLSCREEN
+ bounds = Rect(0, 0, 1080, 2400)
+ activityType = ACTIVITY_TYPE_UNDEFINED
+ }
+ displayId = DISPLAY_ID
+ taskId = 2
+ userId = PRIMARY_USER
+ visible = false
+ isVisible = false
+ isRunning = false
+ numActivities = 0
+ childTaskIds = intArrayOf(3, 4)
+ childTaskNames = arrayOf("", "")
+ childTaskUserIds = intArrayOf(0, 0)
+ childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400), Rect(0, 2400, 1080, 4800))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 83e56da..6ce9cff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR
+import com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_CHORD
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
@@ -47,12 +48,15 @@
import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
+import java.util.function.Consumer
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
@@ -88,7 +92,16 @@
.thenReturn(false)
whenever(userManager.isUserUnlocked).thenReturn(true)
+ // Stub request processor as a synchronous no-op for tests with the flag enabled
+ doAnswer {
+ val request: ScreenshotRequest = it.getArgument(0) as ScreenshotRequest
+ val consumer: Consumer<ScreenshotRequest> = it.getArgument(1)
+ consumer.accept(request)
+ }.`when`(requestProcessor).processAsync(/* request= */ any(), /* callback= */ any())
+
+ // Flipped in selected test cases
flags.set(SCREENSHOT_REQUEST_PROCESSOR, false)
+ flags.set(SCREENSHOT_WORK_PROFILE_POLICY, false)
service.attach(
mContext,
@@ -105,10 +118,10 @@
service.onBind(null /* unused: Intent */)
service.onUnbind(null /* unused: Intent */)
- verify(controller).removeWindow()
+ verify(controller, times(1)).removeWindow()
service.onDestroy()
- verify(controller).onDestroy()
+ verify(controller, times(1)).onDestroy()
}
@Test
@@ -120,7 +133,32 @@
service.handleRequest(request, { /* onSaved */ }, callback)
- verify(controller).takeScreenshotFullscreen(
+ verify(controller, times(1)).takeScreenshotFullscreen(
+ eq(topComponent),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_KEY_CHORD.id)
+ assertEquals("Expected supplied package name",
+ topComponent.packageName, eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshot_requestProcessorEnabled() {
+ flags.set(SCREENSHOT_REQUEST_PROCESSOR, true)
+
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_CHORD,
+ topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller, times(1)).takeScreenshotFullscreen(
eq(topComponent),
/* onSavedListener = */ any(),
/* requestCallback = */ any())
@@ -143,7 +181,7 @@
service.handleRequest(request, { /* onSaved */ }, callback)
- verify(controller).takeScreenshotPartial(
+ verify(controller, times(1)).takeScreenshotPartial(
/* topComponent = */ isNull(),
/* onSavedListener = */ any(),
/* requestCallback = */ any())
@@ -167,7 +205,7 @@
service.handleRequest(request, { /* onSaved */ }, callback)
- verify(controller).handleImageAsScreenshot(
+ verify(controller, times(1)).handleImageAsScreenshot(
argThat { b -> b.equalsHardwareBitmap(bitmap) },
eq(bounds),
eq(Insets.NONE), eq(TASK_ID), eq(USER_ID), eq(topComponent),
@@ -193,8 +231,8 @@
service.handleRequest(request, { /* onSaved */ }, callback)
- verify(notificationsController).notifyScreenshotError(anyInt())
- verify(callback).reportError()
+ verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
+ verify(callback, times(1)).reportError()
verifyZeroInteractions(controller)
}
@@ -217,7 +255,7 @@
service.handleRequest(request, { /* onSaved */ }, callback)
// error shown: Toast.makeText(...).show(), untestable
- verify(callback).reportError()
+ verify(callback, times(1)).reportError()
verifyZeroInteractions(controller)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 98389c2..e2ce939 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -458,7 +458,6 @@
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
- mDumpManager,
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
mInteractionJankMonitor),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index dd2b667..3cc4ee1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,15 +16,8 @@
package com.android.systemui.statusbar;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.content.Intent.ACTION_USER_SWITCHED;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PEOPLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
-
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -60,7 +53,6 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.KeyguardNotificationSuppressor;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -359,93 +351,6 @@
verify(listener, never()).onNotificationStateChanged();
}
- @Test
- public void testShowSilentNotifications_settingSaysShow() {
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_LOW)
- .build();
- entry.setBucket(BUCKET_SILENT);
-
- assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
- }
-
- @Test
- public void testShowSilentNotifications_settingSaysHide() {
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
-
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
- NotificationEntry entry = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_LOW)
- .setNotification(notification)
- .build();
- entry.setBucket(BUCKET_SILENT);
- assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
- }
-
- @Test
- public void testShowSilentNotificationsPeopleBucket_settingSaysHide() {
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
-
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
- NotificationEntry entry = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_LOW)
- .setNotification(notification)
- .build();
- entry.setBucket(BUCKET_PEOPLE);
- assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
- }
-
- @Test
- public void testShowSilentNotificationsMediaBucket_settingSaysHide() {
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
-
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
- NotificationEntry entry = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_LOW)
- .setNotification(notification)
- .build();
- entry.setBucket(BUCKET_MEDIA_CONTROLS);
- // always show media controls, even if they're silent
- assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
- }
-
- @Test
- public void testKeyguardNotificationSuppressors() {
- // GIVEN a notification that should be shown on the lockscreen
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .build();
- entry.setBucket(BUCKET_ALERTING);
-
- // WHEN a suppressor is added that filters out all entries
- FakeKeyguardSuppressor suppressor = new FakeKeyguardSuppressor();
- mLockscreenUserManager.addKeyguardNotificationSuppressor(suppressor);
-
- // THEN it's filtered out
- assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
-
- // WHEN the suppressor no longer filters out entries
- suppressor.setShouldSuppress(false);
-
- // THEN it's no longer filtered out
- assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
- }
-
private class TestNotificationLockscreenUserManager
extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) {
@@ -478,17 +383,4 @@
return mSettingsObserver;
}
}
-
- private static class FakeKeyguardSuppressor implements KeyguardNotificationSuppressor {
- private boolean mShouldSuppress = true;
-
- @Override
- public boolean shouldSuppressOnKeyguard(NotificationEntry entry) {
- return mShouldSuppress;
- }
-
- public void setShouldSuppress(boolean shouldSuppress) {
- mShouldSuppress = shouldSuppress;
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
index 3fc0c81..b719c7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -128,8 +127,6 @@
@Test
public void testNotNotifiedWithoutNotifications() {
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mLockScreenUserManager.shouldHideNotifications(anyInt())).thenReturn(
- true);
mDynamicPrivacyController.onUnlockedChanged();
verifyNoMoreInteractions(mListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
deleted file mode 100644
index 19dd027..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.notification;
-
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class NotificationListControllerTest extends SysuiTestCase {
- private NotificationListController mController;
-
- @Mock private NotificationEntryManager mEntryManager;
- @Mock private NotificationListContainer mListContainer;
- @Mock private DeviceProvisionedController mDeviceProvisionedController;
-
- @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
- @Captor private ArgumentCaptor<DeviceProvisionedListener> mProvisionedCaptor;
-
- private NotificationEntryListener mEntryListener;
- private DeviceProvisionedListener mProvisionedListener;
-
- private int mNextNotifId = 0;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
-
- mController = new NotificationListController(
- mEntryManager,
- mListContainer,
- mDeviceProvisionedController);
- mController.bind();
-
- // Capture callbacks passed to mocks
- verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
- mEntryListener = mEntryListenerCaptor.getValue();
- verify(mDeviceProvisionedController).addCallback(mProvisionedCaptor.capture());
- mProvisionedListener = mProvisionedCaptor.getValue();
- }
-
- @Test
- public void testCleanUpViewStateOnEntryRemoved() {
- final NotificationEntry entry = buildEntry();
- mEntryListener.onEntryRemoved(
- entry,
- NotificationVisibility.obtain(entry.getKey(), 0, 0, true),
- false,
- UNDEFINED_DISMISS_REASON);
- verify(mListContainer).cleanUpViewStateForEntry(entry);
- }
-
- @Test
- public void testCallUpdateNotificationsOnDeviceProvisionedChange() {
- mProvisionedListener.onDeviceProvisionedChanged();
- verify(mEntryManager).updateNotifications(anyString());
- }
-
- private NotificationEntry buildEntry() {
- mNextNotifId++;
-
- Notification.Builder n = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
-
- return new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setId(mNextNotifId)
- .setUid(TEST_UID)
- .setNotification(n.build())
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
- }
-
- private static final String TEST_PACKAGE_NAME = "test";
- private static final int TEST_UID = 0;
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt
deleted file mode 100644
index c17fe6f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * 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.systemui.statusbar.notification.collection.legacy
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.GroupEventDispatcher
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener
-import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
-import com.android.systemui.util.mockito.mock
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@RunWithLooper
-class GroupEventDispatcherTest : SysuiTestCase() {
- val groupMap = mutableMapOf<String, NotificationGroup>()
- val groupTestHelper = NotificationGroupTestHelper(mContext)
-
- private val dispatcher = GroupEventDispatcher(groupMap::get)
- private val listener: OnGroupChangeListener = mock()
-
- @Before
- fun setup() {
- dispatcher.registerGroupChangeListener(listener)
- }
-
- @Test
- fun testOnGroupsChangedUnbuffered() {
- dispatcher.notifyGroupsChanged()
- verify(listener).onGroupsChanged()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testOnGroupsChangedBuffered() {
- dispatcher.openBufferScope()
- dispatcher.notifyGroupsChanged()
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verify(listener).onGroupsChanged()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testOnGroupsChangedDoubleBuffered() {
- dispatcher.openBufferScope()
- dispatcher.notifyGroupsChanged()
- dispatcher.openBufferScope() // open a nested buffer scope
- dispatcher.notifyGroupsChanged()
- dispatcher.closeBufferScope() // should NOT flush events
- dispatcher.notifyGroupsChanged()
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope() // this SHOULD flush events
- verify(listener).onGroupsChanged()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testOnGroupsChangedBufferCoalesces() {
- dispatcher.openBufferScope()
- dispatcher.notifyGroupsChanged()
- dispatcher.notifyGroupsChanged()
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verify(listener).onGroupsChanged()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testOnGroupCreatedIsNeverBuffered() {
- val group = addGroup(1)
-
- dispatcher.openBufferScope()
- dispatcher.notifyGroupCreated(group)
- verify(listener).onGroupCreated(group, group.groupKey)
- verifyNoMoreInteractions(listener)
-
- dispatcher.closeBufferScope()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testOnGroupRemovedIsNeverBuffered() {
- val group = addGroup(1)
-
- dispatcher.openBufferScope()
- dispatcher.notifyGroupRemoved(group)
- verify(listener).onGroupRemoved(group, group.groupKey)
- verifyNoMoreInteractions(listener)
-
- dispatcher.closeBufferScope()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideAddedUnbuffered() {
- val group = addGroup(1)
- val newAlertEntry = groupTestHelper.createChildNotification()
- group.alertOverride = newAlertEntry
- dispatcher.notifyAlertOverrideChanged(group, null)
- verify(listener).onGroupAlertOverrideChanged(group, null, newAlertEntry)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideRemovedUnbuffered() {
- val group = addGroup(1)
- val oldAlertEntry = groupTestHelper.createChildNotification()
- dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
- verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, null)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideChangedUnbuffered() {
- val group = addGroup(1)
- val oldAlertEntry = groupTestHelper.createChildNotification()
- val newAlertEntry = groupTestHelper.createChildNotification()
- group.alertOverride = newAlertEntry
- dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
- verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideChangedBuffered() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- val oldAlertEntry = groupTestHelper.createChildNotification()
- val newAlertEntry = groupTestHelper.createChildNotification()
- group.alertOverride = newAlertEntry
- dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideIgnoredIfRemoved() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- val oldAlertEntry = groupTestHelper.createChildNotification()
- val newAlertEntry = groupTestHelper.createChildNotification()
- group.alertOverride = newAlertEntry
- dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
- verifyNoMoreInteractions(listener)
- groupMap.clear()
- dispatcher.closeBufferScope()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideMultipleChangesBuffered() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- val oldAlertEntry = groupTestHelper.createChildNotification()
- val newAlertEntry = groupTestHelper.createChildNotification()
- group.alertOverride = null
- dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
- group.alertOverride = newAlertEntry
- dispatcher.notifyAlertOverrideChanged(group, null)
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideTemporaryValueSwallowed() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- val stableAlertEntry = groupTestHelper.createChildNotification()
- group.alertOverride = null
- dispatcher.notifyAlertOverrideChanged(group, stableAlertEntry)
- group.alertOverride = stableAlertEntry
- dispatcher.notifyAlertOverrideChanged(group, null)
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testAlertOverrideTemporaryNullSwallowed() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- val temporaryAlertEntry = groupTestHelper.createChildNotification()
- group.alertOverride = temporaryAlertEntry
- dispatcher.notifyAlertOverrideChanged(group, null)
- group.alertOverride = null
- dispatcher.notifyAlertOverrideChanged(group, temporaryAlertEntry)
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testSuppressOnUnbuffered() {
- val group = addGroup(1)
- group.suppressed = true
- dispatcher.notifySuppressedChanged(group)
- verify(listener).onGroupSuppressionChanged(group, true)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testSuppressOffUnbuffered() {
- val group = addGroup(1)
- group.suppressed = false
- dispatcher.notifySuppressedChanged(group)
- verify(listener).onGroupSuppressionChanged(group, false)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testSuppressOnBuffered() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- group.suppressed = false
- dispatcher.notifySuppressedChanged(group)
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verify(listener).onGroupSuppressionChanged(group, false)
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testSuppressOnIgnoredIfRemoved() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- group.suppressed = false
- dispatcher.notifySuppressedChanged(group)
- verifyNoMoreInteractions(listener)
- groupMap.clear()
- dispatcher.closeBufferScope()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testSuppressOnOffBuffered() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- group.suppressed = true
- dispatcher.notifySuppressedChanged(group)
- group.suppressed = false
- dispatcher.notifySuppressedChanged(group)
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verifyNoMoreInteractions(listener)
- }
-
- @Test
- fun testSuppressOnOffOnBuffered() {
- dispatcher.openBufferScope()
- val group = addGroup(1)
- group.suppressed = true
- dispatcher.notifySuppressedChanged(group)
- group.suppressed = false
- dispatcher.notifySuppressedChanged(group)
- group.suppressed = true
- dispatcher.notifySuppressedChanged(group)
- verifyNoMoreInteractions(listener)
- dispatcher.closeBufferScope()
- verify(listener).onGroupSuppressionChanged(group, true)
- verifyNoMoreInteractions(listener)
- }
-
- private fun addGroup(id: Int): NotificationGroup {
- val group = NotificationGroup("group:$id")
- groupMap[group.groupKey] = group
- return group
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt
deleted file mode 100644
index 1fb4ca1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 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.systemui.statusbar.notification.people
-
-object EmptySubscription : Subscription {
- override fun unsubscribe() {}
-}
-
-class FakeDataSource<T>(
- private val data: T,
- private val subscription: Subscription = EmptySubscription
-) : DataSource<T> {
- override fun registerListener(listener: DataListener<T>): Subscription {
- listener.onDataChanged(data)
- return subscription
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
deleted file mode 100644
index 5898664..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.notification.people
-
-import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.ActivityStarter
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import kotlin.reflect.KClass
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PeopleHubViewControllerTest : SysuiTestCase() {
-
- @JvmField @Rule val mockito: MockitoRule = MockitoJUnit.rule()
-
- @Mock private lateinit var mockViewBoundary: PeopleHubViewBoundary
- @Mock private lateinit var mockActivityStarter: ActivityStarter
-
- @Test
- fun testBindViewModelToViewBoundary() {
- val fakePerson1 = fakePersonViewModel("name")
- val fakeViewModel = PeopleHubViewModel(sequenceOf(fakePerson1), true)
-
- val mockFactory = mock(PeopleHubViewModelFactory::class.java)
- whenever(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel)
-
- val mockClickView = mock(View::class.java)
- whenever(mockViewBoundary.associatedViewForClickAnimation).thenReturn(mockClickView)
-
- val fakePersonViewAdapter1 = FakeDataListener<PersonViewModel?>()
- val fakePersonViewAdapter2 = FakeDataListener<PersonViewModel?>()
- whenever(mockViewBoundary.personViewAdapters)
- .thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2))
-
- val adapter = PeopleHubViewAdapterImpl(FakeDataSource(mockFactory))
-
- adapter.bindView(mockViewBoundary)
-
- assertThat(fakePersonViewAdapter1.lastSeen).isEqualTo(Maybe.Just(fakePerson1))
- assertThat(fakePersonViewAdapter2.lastSeen).isEqualTo(Maybe.Just<PersonViewModel?>(null))
- verify(mockViewBoundary).setVisible(true)
- verify(mockFactory).createWithAssociatedClickView(mockClickView)
- }
-
- @Test
- fun testBindViewModelToViewBoundary_moreDataThanCanBeDisplayed_displaysMostRecent() {
- val fakePerson1 = fakePersonViewModel("person1")
- val fakePerson2 = fakePersonViewModel("person2")
- val fakePerson3 = fakePersonViewModel("person3")
- val fakePeople = sequenceOf(fakePerson3, fakePerson2, fakePerson1)
- val fakeViewModel = PeopleHubViewModel(fakePeople, true)
-
- val mockFactory = mock(PeopleHubViewModelFactory::class.java)
- whenever(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel)
-
- whenever(mockViewBoundary.associatedViewForClickAnimation)
- .thenReturn(mock(View::class.java))
-
- val fakePersonViewAdapter1 = FakeDataListener<PersonViewModel?>()
- val fakePersonViewAdapter2 = FakeDataListener<PersonViewModel?>()
- whenever(mockViewBoundary.personViewAdapters)
- .thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2))
-
- val adapter = PeopleHubViewAdapterImpl(FakeDataSource(mockFactory))
-
- adapter.bindView(mockViewBoundary)
-
- assertThat(fakePersonViewAdapter1.lastSeen).isEqualTo(Maybe.Just(fakePerson3))
- assertThat(fakePersonViewAdapter2.lastSeen).isEqualTo(Maybe.Just(fakePerson2))
- }
-
- @Test
- fun testViewModelDataSourceTransformsModel() {
- val fakeClickRunnable = mock(Runnable::class.java)
- val fakePerson = fakePersonModel("id", "name", fakeClickRunnable)
- val fakeModel = PeopleHubModel(listOf(fakePerson))
- val fakeModelDataSource = FakeDataSource(fakeModel)
- val factoryDataSource = PeopleHubViewModelFactoryDataSourceImpl(
- mockActivityStarter,
- fakeModelDataSource
- )
- val fakeListener = FakeDataListener<PeopleHubViewModelFactory>()
- val mockClickView = mock(View::class.java)
-
- factoryDataSource.registerListener(fakeListener)
-
- val viewModel = (fakeListener.lastSeen as Maybe.Just).value
- .createWithAssociatedClickView(mockClickView)
- assertThat(viewModel.isVisible).isTrue()
- val people = viewModel.people.toList()
- assertThat(people.size).isEqualTo(1)
- assertThat(people[0].name).isEqualTo("name")
- assertThat(people[0].icon).isSameInstanceAs(fakePerson.avatar)
-
- people[0].onClick()
-
- verify(fakeClickRunnable).run()
- }
-}
-
-/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
-private inline fun <reified T : Any> any(): T {
- return Mockito.any() ?: createInstance(T::class)
-}
-
-/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
-private inline fun <reified T : Any> same(value: T): T {
- return Mockito.same(value) ?: createInstance(T::class)
-}
-
-/** Creates an instance of the given class. */
-private fun <T : Any> createInstance(clazz: KClass<T>): T = castNull()
-
-/** Tricks the Kotlin compiler into assigning `null` to a non-nullable variable. */
-@Suppress("UNCHECKED_CAST")
-private fun <T> castNull(): T = null as T
-
-private fun fakePersonModel(
- id: String,
- name: CharSequence,
- clickRunnable: Runnable,
- userId: Int = 0
-): PersonModel =
- PersonModel(id, userId, name, mock(Drawable::class.java), clickRunnable)
-
-private fun fakePersonViewModel(name: CharSequence): PersonViewModel =
- PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass))
-
-sealed class Maybe<T> {
- data class Just<T>(val value: T) : Maybe<T>()
- class Nothing<T> : Maybe<T>() {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
- return true
- }
-
- override fun hashCode(): Int {
- return javaClass.hashCode()
- }
- }
-}
-
-class FakeDataListener<T> : DataListener<T> {
-
- var lastSeen: Maybe<T> = Maybe.Nothing()
-
- override fun onDataChanged(data: T) {
- lastSeen = Maybe.Just(data)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
deleted file mode 100644
index b20f95c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright (C) 2018 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.systemui.statusbar.notification.row;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
-
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.NotificationChannel;
-import android.content.Context;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link NotificationBlockingHelperManager}.
- */
-@SmallTest
-@FlakyTest
-@org.junit.runner.RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
- private NotificationBlockingHelperManager mBlockingHelperManager;
-
- private NotificationTestHelper mHelper;
-
- @Mock private NotificationGutsManager mGutsManager;
- @Mock private NotificationEntryManager mEntryManager;
- @Mock private NotificationMenuRow mMenuRow;
- @Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
- @Mock private GroupMembershipManager mGroupMembershipManager;
-
- @Before
- public void setUp() {
- allowTestableLooperAsMainThread();
- MockitoAnnotations.initMocks(this);
- when(mGutsManager.openGuts(
- any(View.class),
- anyInt(),
- anyInt(),
- any(NotificationMenuRowPlugin.MenuItem.class)))
- .thenReturn(true);
- when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
-
- mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
-
- mBlockingHelperManager = new NotificationBlockingHelperManager(
- mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class),
- mGroupMembershipManager);
- // By default, have the shade visible/expanded.
- mBlockingHelperManager.setNotificationShadeExpanded(1f);
- }
-
- @Test
- public void testDismissCurrentBlockingHelper_nullBlockingHelperRow() {
- // By default, this shouldn't dismiss (no pointers/vars set up!)
- assertFalse(mBlockingHelperManager.dismissCurrentBlockingHelper());
- assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
- }
-
- @Test
- public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception {
- ExpandableNotificationRow row = createBlockableRowSpy();
- row.setBlockingHelperShowing(true);
- when(row.isAttachedToWindow()).thenReturn(false);
- mBlockingHelperManager.setBlockingHelperRowForTest(row);
-
- assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
- assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
-
- verify(mEntryManager, times(0)).updateNotifications(anyString());
- }
-
- @Test
- public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception {
- ExpandableNotificationRow row = createBlockableRowSpy();
- row.setBlockingHelperShowing(true);
- when(row.isAttachedToWindow()).thenReturn(true);
- mBlockingHelperManager.setBlockingHelperRowForTest(row);
-
- assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
- assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
-
- verify(mEntryManager).updateNotifications(anyString());
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_shown() throws Exception {
- ExpandableNotificationRow row = createBlockableRowSpy();
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
-
- assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-
- verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_notShownForMultiChannelGroup() throws Exception {
- ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
- int i = 0;
- for (ExpandableNotificationRow childRow : groupRow.getAttachedChildren()) {
- modifyRanking(childRow.getEntry())
- .setChannel(
- new NotificationChannel(
- Integer.toString(i++), "", IMPORTANCE_DEFAULT))
- .build();
- }
-
- modifyRanking(groupRow.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
-
- assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
-
- verify(mGutsManager, never()).openGuts(groupRow, 0, 0, mMenuItem);
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception {
- ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
- modifyRanking(groupRow.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
-
- assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
-
- verify(mGutsManager).openGuts(groupRow, 0, 0, mMenuItem);
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification()
- throws Exception {
- ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(1);
- // Explicitly get the children container & call getViewAtPosition on it instead of the row
- // as other factors such as view expansion may cause us to get the parent row back instead
- // of the child row.
- ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
- modifyRanking(childRow.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
- assertFalse(childRow.getIsNonblockable());
-
- when(mGroupMembershipManager.isOnlyChildInGroup(childRow.getEntry())).thenReturn(true);
- assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
-
- verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception {
- ExpandableNotificationRow row = createBlockableRowSpy();
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEUTRAL)
- .build();
-
- assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment()
- throws Exception {
- ExpandableNotificationRow row = createBlockableRowSpy();
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_POSITIVE)
- .build();
-
- assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception {
- ExpandableNotificationRow row = createBlockableRowSpy();
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
- // Hide the shade
- mBlockingHelperManager.setNotificationShadeExpanded(0f);
-
- assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_notShownDueToNonblockability() throws Exception {
- ExpandableNotificationRow row = createBlockableRowSpy();
- when(row.getIsNonblockable()).thenReturn(true);
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
-
- assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
- }
-
- @Test
- public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup()
- throws Exception {
- ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(2);
- // Explicitly get the children container & call getViewAtPosition on it instead of the row
- // as other factors such as view expansion may cause us to get the parent row back instead
- // of the child row.
- ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
- modifyRanking(childRow.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
-
- assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
- }
-
- @Test
- public void testBlockingHelperShowAndDismiss() throws Exception{
- ExpandableNotificationRow row = createBlockableRowSpy();
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
- when(row.isAttachedToWindow()).thenReturn(true);
-
- // Show check
- assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-
- verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
-
- // Dismiss check
- assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
- assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
-
- verify(mEntryManager).updateNotifications(anyString());
- }
-
- @Test
- public void testNonBlockable_package() {
- mBlockingHelperManager.setNonBlockablePkgs(new String[] {"banana", "strawberry:pie"});
-
- assertFalse(mBlockingHelperManager.isNonblockable("orange", "pie"));
-
- assertTrue(mBlockingHelperManager.isNonblockable("banana", "pie"));
- }
-
- @Test
- public void testNonBlockable_channel() {
- mBlockingHelperManager.setNonBlockablePkgs(new String[] {"banana", "strawberry:pie"});
-
- assertFalse(mBlockingHelperManager.isNonblockable("strawberry", "shortcake"));
-
- assertTrue(mBlockingHelperManager.isNonblockable("strawberry", "pie"));
- }
-
- private ExpandableNotificationRow createBlockableRowSpy() throws Exception {
- ExpandableNotificationRow row = spy(mHelper.createRow());
- when(row.getIsNonblockable()).thenReturn(false);
- return row;
- }
-
- private ExpandableNotificationRow createBlockableGroupRowSpy(int numChildren) throws Exception {
- ExpandableNotificationRow row = spy(mHelper.createGroup(numChildren));
- when(row.getIsNonblockable()).thenReturn(false);
- return row;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 1460e04..966e233 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -34,7 +34,6 @@
import android.content.res.Resources;
import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
import androidx.test.filters.SmallTest;
@@ -42,11 +41,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -61,12 +58,9 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
@@ -112,7 +106,6 @@
@Mock private KeyguardMediaController mKeyguardMediaController;
@Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
@Mock private KeyguardBypassController mKeyguardBypassController;
- @Mock private SysuiColorExtractor mColorExtractor;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
@Mock private DumpManager mDumpManager;
@@ -122,19 +115,14 @@
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private CentralSurfaces mCentralSurfaces;
@Mock private ScrimController mScrimController;
- @Mock private NotificationGroupManagerLegacy mGroupManagerLegacy;
@Mock private GroupExpansionManager mGroupExpansionManager;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotifCollection mNotifCollection;
@Mock private NotificationEntryManager mEntryManager;
- @Mock private IStatusBarService mIStatusBarService;
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock private LayoutInflater mLayoutInflater;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private ShadeController mShadeController;
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private StackStateLogger mStackLogger;
@@ -176,7 +164,6 @@
mNotificationSwipeHelperBuilder,
mCentralSurfaces,
mScrimController,
- mGroupManagerLegacy,
mGroupExpansionManager,
mSilentHeaderController,
mNotifPipeline,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 8fd6842..35d2363b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -3,19 +3,21 @@
import android.annotation.DimenRes
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.EmptyShadeView
+import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController
-import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
-import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
@@ -26,17 +28,20 @@
private val hostView = FrameLayout(context)
private val stackScrollAlgorithm = StackScrollAlgorithm(context, hostView)
- private val expandableViewState = ExpandableViewState()
private val notificationRow = mock(ExpandableNotificationRow::class.java)
private val dumpManager = mock(DumpManager::class.java)
private val mStatusBarKeyguardViewManager = mock(StatusBarKeyguardViewManager::class.java)
+ private val notificationShelf = mock(NotificationShelf::class.java)
+ private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
+ layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
+ }
private val ambientState = AmbientState(
- context,
- dumpManager,
- SectionProvider { _, _ -> false },
- BypassController { false },
- mStatusBarKeyguardViewManager
+ context,
+ dumpManager,
+ /* sectionProvider */ { _, _ -> false },
+ /* bypassController */ { false },
+ mStatusBarKeyguardViewManager
)
private val testableResources = mContext.orCreateTestableResources
@@ -49,7 +54,9 @@
@Before
fun setUp() {
- whenever(notificationRow.viewState).thenReturn(expandableViewState)
+ whenever(notificationShelf.viewState).thenReturn(ExpandableViewState())
+ whenever(notificationRow.viewState).thenReturn(ExpandableViewState())
+
hostView.addView(notificationRow)
}
@@ -60,7 +67,8 @@
stackScrollAlgorithm.resetViewStates(ambientState, 0)
- assertThat(expandableViewState.yTranslation).isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+ assertThat(notificationRow.viewState.yTranslation)
+ .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
}
@Test
@@ -75,15 +83,12 @@
stackScrollAlgorithm.resetViewStates(ambientState, 0)
// top margin presence should decrease heads up translation up to minHeadsUpTranslation
- assertThat(expandableViewState.yTranslation).isEqualTo(minHeadsUpTranslation)
+ assertThat(notificationRow.viewState.yTranslation).isEqualTo(minHeadsUpTranslation)
}
@Test
fun resetViewStates_emptyShadeView_isCenteredVertically() {
stackScrollAlgorithm.initView(context)
- val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
- layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
- }
hostView.removeAllViews()
hostView.addView(emptyShadeView)
ambientState.layoutMaxHeight = 1280
@@ -98,6 +103,121 @@
}
@Test
+ fun resetViewStates_hunGoingToShade_viewBecomesOpaque() {
+ whenever(notificationRow.isAboveShelf).thenReturn(true)
+ ambientState.isShadeExpanded = true
+ ambientState.trackedHeadsUpRow = notificationRow
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun resetViewStates_isExpansionChanging_viewBecomesTransparent() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ ambientState.isExpansionChanging = true
+ ambientState.expansionFraction = 0.25f
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val expected = getContentAlpha(0.25f)
+ assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ }
+
+ @Test
+ fun resetViewStates_isExpansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ ambientState.isExpansionChanging = true
+ ambientState.expansionFraction = 0.25f
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val expected = aboutToShowBouncerProgress(0.25f)
+ assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ }
+
+ @Test
+ fun resetViewStates_isOnKeyguard_viewBecomesTransparent() {
+ ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+ ambientState.hideAmount = 0.25f
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.alpha).isEqualTo(1f - ambientState.hideAmount)
+ }
+
+ @Test
+ fun resetViewStates_isOnKeyguard_emptyShadeViewBecomesTransparent() {
+ ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+ ambientState.fractionToShade = 0.25f
+ stackScrollAlgorithm.initView(context)
+ hostView.removeAllViews()
+ hostView.addView(emptyShadeView)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val expected = getContentAlpha(ambientState.fractionToShade)
+ assertThat(emptyShadeView.viewState.alpha).isEqualTo(expected)
+ }
+
+ @Test
+ fun resetViewStates_isOnKeyguard_emptyShadeViewBecomesOpaque() {
+ ambientState.setStatusBarState(StatusBarState.SHADE)
+ ambientState.fractionToShade = 0.25f
+ stackScrollAlgorithm.initView(context)
+ hostView.removeAllViews()
+ hostView.addView(emptyShadeView)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(emptyShadeView.viewState.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun resetViewStates_hiddenShelf_viewAlphaDoesNotChange() {
+ val expected = notificationShelf.viewState.alpha
+ notificationShelf.viewState.hidden = true
+ ambientState.shelf = notificationShelf
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationShelf.viewState.alpha).isEqualTo(expected)
+ }
+
+ @Test
+ fun resetViewStates_shelfTopLessThanViewTop_hidesView() {
+ notificationRow.viewState.yTranslation = 10f
+ notificationShelf.viewState.yTranslation = 0.9f
+ notificationShelf.viewState.hidden = false
+ ambientState.shelf = notificationShelf
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.alpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun resetViewStates_shelfTopGreaterOrEqualThanViewTop_viewAlphaDoesNotChange() {
+ val expected = notificationRow.viewState.alpha
+ notificationRow.viewState.yTranslation = 10f
+ notificationShelf.viewState.yTranslation = 10f
+ notificationShelf.viewState.hidden = false
+ ambientState.shelf = notificationShelf
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ }
+
+ @Test
fun getGapForLocation_onLockscreen_returnsSmallGap() {
val gap = stackScrollAlgorithm.getGapForLocation(
/* fractionToShade= */ 0f, /* onKeyguard= */ true)
@@ -267,7 +387,6 @@
assertEquals(10f, expandableViewState.yTranslation)
}
-
@Test
fun clampHunToTop_viewYFarAboveVisibleStack_heightCollapsed() {
val expandableViewState = ExpandableViewState()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index a0f7087..735a88d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -133,7 +133,6 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
@@ -209,7 +208,6 @@
@Mock private DozeScrimController mDozeScrimController;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
- @Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationListener mNotificationListener;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index a6b7e51..ca98143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -58,7 +58,6 @@
@Before
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- mDependency.injectMockDependency(DarkIconDispatcher.class);
}
@Test
@@ -75,7 +74,8 @@
layout,
mock(FeatureFlags.class),
mock(StatusBarPipelineFlags.class),
- () -> mock(WifiViewModel.class));
+ () -> mock(WifiViewModel.class),
+ mock(DarkIconDispatcher.class));
testCallOnAdd_forManager(manager);
}
@@ -116,8 +116,10 @@
LinearLayout group,
FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
- Provider<WifiViewModel> wifiViewModelProvider) {
- super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
+ Provider<WifiViewModel> wifiViewModelProvider,
+ DarkIconDispatcher darkIconDispatcher) {
+ super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider,
+ darkIconDispatcher);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
index 9829271..d070ba0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
@@ -473,6 +473,40 @@
job.cancel()
}
+ /** Regression test for b/244173280. */
+ @Test
+ fun wifiNetwork_multipleSubscribers_newSubscribersGetCurrentValue() = runBlocking(IMMEDIATE) {
+ var latest1: WifiNetworkModel? = null
+ val job1 = underTest
+ .wifiNetwork
+ .onEach { latest1 = it }
+ .launchIn(this)
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+ assertThat(latest1 is WifiNetworkModel.Active).isTrue()
+ val latest1Active = latest1 as WifiNetworkModel.Active
+ assertThat(latest1Active.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latest1Active.ssid).isEqualTo(SSID)
+
+ // WHEN we add a second subscriber after having already emitted a value
+ var latest2: WifiNetworkModel? = null
+ val job2 = underTest
+ .wifiNetwork
+ .onEach { latest2 = it }
+ .launchIn(this)
+
+ // THEN the second subscribe receives the already-emitted value
+ assertThat(latest2 is WifiNetworkModel.Active).isTrue()
+ val latest2Active = latest2 as WifiNetworkModel.Active
+ assertThat(latest2Active.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latest2Active.ssid).isEqualTo(SSID)
+
+ job1.cancel()
+ job2.cancel()
+ }
+
@Test
fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) {
underTest = WifiRepositoryImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
new file mode 100644
index 0000000..092e82c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.systemui.util.kotlin
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PairwiseFlowTest : SysuiTestCase() {
+ @Test
+ fun simple() = runBlocking {
+ assertThatFlow((1..3).asFlow().pairwise())
+ .emitsExactly(
+ WithPrev(1, 2),
+ WithPrev(2, 3),
+ )
+ }
+
+ @Test
+ fun notEnough() = runBlocking {
+ assertThatFlow(flowOf(1).pairwise()).emitsNothing()
+ }
+
+ @Test
+ fun withInit() = runBlocking {
+ assertThatFlow(flowOf(2).pairwise(initialValue = 1))
+ .emitsExactly(WithPrev(1, 2))
+ }
+
+ @Test
+ fun notEnoughWithInit() = runBlocking {
+ assertThatFlow(emptyFlow<Int>().pairwise(initialValue = 1)).emitsNothing()
+ }
+
+ @Test
+ fun withStateFlow() = runBlocking(Dispatchers.Main.immediate) {
+ val state = MutableStateFlow(1)
+ val stop = MutableSharedFlow<Unit>()
+
+ val stoppable = merge(state, stop)
+ .takeWhile { it is Int }
+ .filterIsInstance<Int>()
+
+ val job1 = launch {
+ assertThatFlow(stoppable.pairwise()).emitsExactly(WithPrev(1, 2))
+ }
+ state.value = 2
+ val job2 = launch { assertThatFlow(stoppable.pairwise()).emitsNothing() }
+
+ stop.emit(Unit)
+
+ assertThatJob(job1).isCompleted()
+ assertThatJob(job2).isCompleted()
+ }
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class SetChangesFlowTest : SysuiTestCase() {
+ @Test
+ fun simple() = runBlocking {
+ assertThatFlow(
+ flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges()
+ ).emitsExactly(
+ SetChanges(
+ added = setOf(1, 2, 3),
+ removed = emptySet(),
+ ),
+ SetChanges(
+ added = setOf(4),
+ removed = setOf(1),
+ ),
+ )
+ }
+
+ @Test
+ fun onlyOneEmission() = runBlocking {
+ assertThatFlow(flowOf(setOf(1)).setChanges())
+ .emitsExactly(
+ SetChanges(
+ added = setOf(1),
+ removed = emptySet(),
+ )
+ )
+ }
+
+ @Test
+ fun fromEmptySet() = runBlocking {
+ assertThatFlow(flowOf(emptySet(), setOf(1, 2)).setChanges())
+ .emitsExactly(
+ SetChanges(
+ removed = emptySet(),
+ added = setOf(1, 2),
+ )
+ )
+ }
+}
+
+private fun <T> assertThatFlow(flow: Flow<T>) = object {
+ suspend fun emitsExactly(vararg emissions: T) =
+ assertThat(flow.toList()).containsExactly(*emissions).inOrder()
+ suspend fun emitsNothing() =
+ assertThat(flow.toList()).isEmpty()
+}
+
+private fun assertThatJob(job: Job) = object {
+ fun isCompleted() = assertThat(job.isCompleted).isTrue()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 312db2d..2e74bf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -85,6 +85,8 @@
@Mock
MediaOutputDialogFactory mMediaOutputDialogFactory;
@Mock
+ VolumePanelFactory mVolumePanelFactory;
+ @Mock
ActivityStarter mActivityStarter;
@Mock
InteractionJankMonitor mInteractionJankMonitor;
@@ -102,6 +104,7 @@
mDeviceProvisionedController,
mConfigurationController,
mMediaOutputDialogFactory,
+ mVolumePanelFactory,
mActivityStarter,
mInteractionJankMonitor);
mDialog.init(0, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 5d63632..09da52e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -99,7 +99,6 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -162,8 +161,6 @@
@Mock
private CommonNotifCollection mCommonNotifCollection;
@Mock
- private NotificationGroupManagerLegacy mNotificationGroupManager;
- @Mock
private BubblesManager.NotifCallback mNotifCallback;
@Mock
private WindowManager mWindowManager;
@@ -389,7 +386,6 @@
interruptionStateProvider,
mZenModeController,
mLockscreenUserManager,
- mNotificationGroupManager,
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b53ad0a..c56fdb1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -16,14 +16,12 @@
package com.android.systemui.flags
-import android.util.SparseArray
-import android.util.SparseBooleanArray
-import androidx.core.util.containsKey
-
class FakeFeatureFlags : FeatureFlags {
- private val booleanFlags = SparseBooleanArray()
- private val stringFlags = SparseArray<String>()
+ private val booleanFlags = mutableMapOf<Int, Boolean>()
+ private val stringFlags = mutableMapOf<Int, String>()
private val knownFlagNames = mutableMapOf<Int, String>()
+ private val flagListeners = mutableMapOf<Int, MutableSet<FlagListenable.Listener>>()
+ private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>()
init {
Flags.getFlagFields().forEach { field ->
@@ -33,27 +31,52 @@
}
fun set(flag: BooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: DeviceConfigBooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: ResourceBooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: SysPropBooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: StringFlag, value: String) {
- stringFlags.put(flag.id, value)
+ if (stringFlags.put(flag.id, value)?.let { value != it } == null) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: ResourceStringFlag, value: String) {
- stringFlags.put(flag.id, value)
+ if (stringFlags.put(flag.id, value)?.let { value != it } == null) {
+ notifyFlagChanged(flag)
+ }
+ }
+
+ private fun notifyFlagChanged(flag: Flag<*>) {
+ flagListeners[flag.id]?.let { listeners ->
+ listeners.forEach { listener ->
+ listener.onFlagChanged(
+ object : FlagListenable.FlagEvent {
+ override val flagId = flag.id
+ override fun requestNoRestart() {}
+ }
+ )
+ }
+ }
}
override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)
@@ -70,25 +93,30 @@
override fun getString(flag: ResourceStringFlag): String = requireStringValue(flag.id)
- override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {}
+ override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
+ flagListeners.getOrPut(flag.id) { mutableSetOf() }.add(listener)
+ listenerFlagIds.getOrPut(listener) { mutableSetOf() }.add(flag.id)
+ }
- override fun removeListener(listener: FlagListenable.Listener) {}
+ override fun removeListener(listener: FlagListenable.Listener) {
+ listenerFlagIds.remove(listener)?.let {
+ flagIds -> flagIds.forEach {
+ id -> flagListeners[id]?.remove(listener)
+ }
+ }
+ }
private fun flagName(flagId: Int): String {
return knownFlagNames[flagId] ?: "UNKNOWN(id=$flagId)"
}
private fun requireBooleanValue(flagId: Int): Boolean {
- if (!booleanFlags.containsKey(flagId)) {
- throw IllegalStateException("Flag ${flagName(flagId)} was accessed but not specified.")
- }
return booleanFlags[flagId]
+ ?: error("Flag ${flagName(flagId)} was accessed but not specified.")
}
private fun requireStringValue(flagId: Int): String {
- if (!stringFlags.containsKey(flagId)) {
- throw IllegalStateException("Flag ${flagName(flagId)} was accessed but not specified.")
- }
return stringFlags[flagId]
+ ?: error("Flag ${flagName(flagId)} was accessed but not specified.")
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6a6d2bb..2b858ad 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -683,8 +683,8 @@
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
FLAGS_PACKAGE_BROADCAST_RECEIVER,
- "intent=" + intent + ";packages=" + packages + ";uid=" + uid
- + ";doit=" + doit);
+ "intent=" + intent + ";packages=" + Arrays.toString(packages)
+ + ";uid=" + uid + ";doit=" + doit);
}
synchronized (mLock) {
final int userId = getChangingUserId();
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
index f731c44..d20fa8e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -447,7 +447,7 @@
StringBuilder builder = new StringBuilder(super.toString());
if (getState() != STATE_GESTURE_CANCELED) {
builder.append(", mBase: ")
- .append(mBase.toString())
+ .append(Arrays.toString(mBase))
.append(", mMinPixelsBetweenSamplesX:")
.append(mMinPixelsBetweenSamplesX)
.append(", mMinPixelsBetweenSamplesY:")
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 907daa3..aec5f5e 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -2830,7 +2830,7 @@
+ " includekeyvalue="
+ doKeyValue
+ " pkgs="
- + pkgList));
+ + Arrays.toString(pkgList)));
}
Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning adb backup..."));
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 5dacdb4..d0300ff 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -25,9 +25,9 @@
import android.annotation.Nullable;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.BackupTransport;
+import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
-import android.app.compat.CompatChanges;
import android.compat.annotation.Overridable;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -46,6 +46,7 @@
import com.google.android.collect.Sets;
+import java.util.Arrays;
import java.util.Set;
/**
@@ -360,8 +361,8 @@
}
if (DEBUG) {
- Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device="
- + signingInfo.getApkContentsSigners());
+ Slog.v(TAG, "signaturesMatch(): stored=" + Arrays.toString(storedSigs)
+ + " device=" + Arrays.toString(signingInfo.getApkContentsSigners()));
}
final int nStored = storedSigs.length;
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 3d8dc14..bd1ecb2 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1305,4 +1305,7 @@
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
*/
public abstract @SignatureResult int checkUidSignaturesForAllUsers(int uid1, int uid2);
+
+ public abstract void setPackageStoppedState(@NonNull String packageName, boolean stopped,
+ @UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6aa472f..a580f98 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4236,9 +4236,8 @@
// Service is now being launched, its package can't be stopped.
try {
- AppGlobals.getPackageManager().setPackageStoppedState(
+ mAm.mPackageManagerInt.setPackageStoppedState(
r.packageName, false, r.userId);
- } catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.packageName + ": " + e);
@@ -4248,7 +4247,8 @@
final String procName = r.processName;
HostingRecord hostingRecord = new HostingRecord(
HostingRecord.HOSTING_TYPE_SERVICE, r.instanceName,
- r.definingPackageName, r.definingUid, r.serviceInfo.processName);
+ r.definingPackageName, r.definingUid, r.serviceInfo.processName,
+ getHostingRecordTriggerType(r));
ProcessRecord app;
if (!isolated) {
@@ -4358,6 +4358,14 @@
return null;
}
+ private String getHostingRecordTriggerType(ServiceRecord r) {
+ if (Manifest.permission.BIND_JOB_SERVICE.equals(r.permission)
+ && r.mRecentCallingUid == SYSTEM_UID) {
+ return HostingRecord.TRIGGER_TYPE_JOB;
+ }
+ return HostingRecord.TRIGGER_TYPE_UNKNOWN;
+ }
+
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
throws TransactionTooLargeException {
for (int i=r.bindings.size()-1; i>=0; i--) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 13a13f2..5e7d814 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -146,6 +146,7 @@
static final String KEY_NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
+ private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
@@ -325,6 +326,14 @@
*/
private static final String KEY_PROCESS_KILL_TIMEOUT = "process_kill_timeout";
+ /**
+ * {@code true} to send in-flight alarm broadcasts ahead of non-alarms; {@code false}
+ * to queue alarm broadcasts identically to non-alarms [i.e. the pre-U behavior]; or
+ * {@code null} or empty string in order to fall back to whatever the build-time default
+ * was for the device.
+ */
+ private static final String KEY_PRIORITIZE_ALARM_BROADCASTS = "prioritize_alarm_broadcasts";
+
private static final String KEY_DEFER_BOOT_COMPLETED_BROADCAST =
"defer_boot_completed_broadcast";
@@ -667,6 +676,12 @@
DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST;
/**
+ * Whether alarm broadcasts are delivered immediately, or queued along with the rest
+ * of the pending ordered broadcasts.
+ */
+ volatile boolean mPrioritizeAlarmBroadcasts = DEFAULT_PRIORITIZE_ALARM_BROADCASTS;
+
+ /**
* How long the Context.startForegroundService() grace period is to get around to
* calling Service.startForeground() before we generate ANR.
*/
@@ -977,6 +992,9 @@
case KEY_PROCESS_KILL_TIMEOUT:
updateProcessKillTimeout();
break;
+ case KEY_PRIORITIZE_ALARM_BROADCASTS:
+ updatePrioritizeAlarmBroadcasts();
+ break;
case KEY_DEFER_BOOT_COMPLETED_BROADCAST:
updateDeferBootCompletedBroadcast();
break;
@@ -1446,6 +1464,17 @@
}
}
+ private void updatePrioritizeAlarmBroadcasts() {
+ // Flag value can be something that evaluates to `true` or `false`,
+ // or empty/null. If it's empty/null, the platform default is used.
+ final String flag = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_PRIORITIZE_ALARM_BROADCASTS,
+ "");
+ mPrioritizeAlarmBroadcasts = TextUtils.isEmpty(flag)
+ ? DEFAULT_PRIORITIZE_ALARM_BROADCASTS
+ : Boolean.parseBoolean(flag);
+ }
private void updateDeferBootCompletedBroadcast() {
mDeferBootCompletedBroadcast = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1786,6 +1815,8 @@
pw.print("="); pw.println(mComponentAliasOverrides);
pw.print(" "); pw.print(KEY_DEFER_BOOT_COMPLETED_BROADCAST);
pw.print("="); pw.println(mDeferBootCompletedBroadcast);
+ pw.print(" "); pw.print(KEY_PRIORITIZE_ALARM_BROADCASTS);
+ pw.print("="); pw.println(mPrioritizeAlarmBroadcasts);
pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED);
pw.print("="); pw.println(mNoKillCachedProcessesUntilBootCompleted);
pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index be0335e..bd776c1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -659,7 +659,7 @@
final BroadcastQueue mFgOffloadBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
// so that dispatch of foreground broadcasts gets precedence.
- final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[4];
+ final BroadcastQueue[] mBroadcastQueues;
@GuardedBy("this")
BroadcastStats mLastBroadcastStats;
@@ -670,25 +670,33 @@
TraceErrorLogger mTraceErrorLogger;
BroadcastQueue broadcastQueueForIntent(Intent intent) {
- if (isOnFgOffloadQueue(intent.getFlags())) {
+ return broadcastQueueForFlags(intent.getFlags(), intent);
+ }
+
+ BroadcastQueue broadcastQueueForFlags(int flags) {
+ return broadcastQueueForFlags(flags, null);
+ }
+
+ BroadcastQueue broadcastQueueForFlags(int flags, Object cookie) {
+ if (isOnFgOffloadQueue(flags)) {
if (DEBUG_BROADCAST_BACKGROUND) {
Slog.i(TAG_BROADCAST,
- "Broadcast intent " + intent + " on foreground offload queue");
+ "Broadcast intent " + cookie + " on foreground offload queue");
}
return mFgOffloadBroadcastQueue;
}
- if (isOnBgOffloadQueue(intent.getFlags())) {
+ if (isOnBgOffloadQueue(flags)) {
if (DEBUG_BROADCAST_BACKGROUND) {
Slog.i(TAG_BROADCAST,
- "Broadcast intent " + intent + " on background offload queue");
+ "Broadcast intent " + cookie + " on background offload queue");
}
return mBgOffloadBroadcastQueue;
}
- final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
+ final boolean isFg = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
- "Broadcast intent " + intent + " on "
+ "Broadcast intent " + cookie + " on "
+ (isFg ? "foreground" : "background") + " queue");
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
@@ -2323,7 +2331,7 @@
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, handlerThread);
mIntentFirewall = null;
- mProcessStats = null;
+ mProcessStats = new ProcessStatsService(this, mContext.getCacheDir());
mCpHelper = new ContentProviderHelper(this, false);
mServices = null;
mSystemThread = null;
@@ -2343,6 +2351,7 @@
mPendingStartActivityUids = new PendingStartActivityUids();
mUseFifoUiScheduling = false;
mEnableOffloadQueue = false;
+ mBroadcastQueues = new BroadcastQueue[0];
mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue =
mFgOffloadBroadcastQueue = null;
mComponentAliasResolver = new ComponentAliasResolver(this);
@@ -2401,6 +2410,7 @@
mEnableOffloadQueue = SystemProperties.getBoolean(
"persist.device_config.activity_manager_native_boot.offload_queue_enabled", true);
+ mBroadcastQueues = new BroadcastQueue[4];
mFgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
@@ -6727,9 +6737,8 @@
// TODO: how set package stopped state should work for sdk sandboxes?
if (!isSdkSandbox) {
try {
- AppGlobals.getPackageManager().setPackageStoppedState(
+ mPackageManagerInt.setPackageStoppedState(
info.packageName, false, UserHandle.getUserId(app.uid));
- } catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ info.packageName + ": " + e);
@@ -12845,9 +12854,8 @@
// !!! TODO: currently no check here that we're already bound
// Backup agent is now in use, its package can't be stopped.
try {
- AppGlobals.getPackageManager().setPackageStoppedState(
+ mPackageManagerInt.setPackageStoppedState(
app.packageName, false, UserHandle.getUserId(app.uid));
- } catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ app.packageName + ": " + e);
@@ -13328,20 +13336,18 @@
final long origId = Binder.clearCallingIdentity();
try {
boolean doTrim = false;
-
synchronized(this) {
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl != null) {
final BroadcastRecord r = rl.curBroadcast;
- if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
+ if (r != null) {
final boolean doNext = r.queue.finishReceiverLocked(
- r, r.resultCode, r.resultData, r.resultExtras,
+ receiver.asBinder(), r.resultCode, r.resultData, r.resultExtras,
r.resultAbort, false);
if (doNext) {
doTrim = true;
}
}
-
if (rl.app != null) {
rl.app.mReceivers.removeReceiver(rl);
}
@@ -14518,22 +14524,10 @@
boolean doNext = false;
BroadcastRecord r;
BroadcastQueue queue;
-
synchronized(this) {
- if (isOnFgOffloadQueue(flags)) {
- queue = mFgOffloadBroadcastQueue;
- } else if (isOnBgOffloadQueue(flags)) {
- queue = mBgOffloadBroadcastQueue;
- } else {
- queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
- ? mFgBroadcastQueue : mBgBroadcastQueue;
- }
-
- r = queue.getMatchingOrderedReceiver(who);
- if (r != null) {
- doNext = r.queue.finishReceiverLocked(r, resultCode,
+ queue = broadcastQueueForFlags(flags);
+ doNext = queue.finishReceiverLocked(who, resultCode,
resultData, resultExtras, resultAbort, true);
- }
// updateOomAdjLocked() will be done here
trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
}
@@ -17881,6 +17875,13 @@
}
/**
+ * Reset the dropbox rate limiter
+ */
+ void resetDropboxRateLimiter() {
+ mDropboxRateLimiter.reset();
+ }
+
+ /**
* Kill processes for the user with id userId and that depend on the package named packageName
*/
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 36908ce..a42b2a4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -363,6 +363,8 @@
return runGetBgRestrictionLevel(pw);
case "observe-foreground-process":
return runGetCurrentForegroundProcess(pw, mInternal, mTaskInterface);
+ case "reset-dropbox-rate-limiter":
+ return runResetDropboxRateLimiter();
default:
return handleDefaultCommands(cmd);
}
@@ -3577,6 +3579,11 @@
return 0;
}
+ int runResetDropboxRateLimiter() throws RemoteException {
+ mInternal.resetDropboxRateLimiter();
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index e9a36e0..4c44343 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -181,7 +181,7 @@
for (int i = 0; i < numEntries; i++) {
if (recipientUid == mDeferredBroadcasts.get(i).uid) {
Deferrals d = mDeferredBroadcasts.remove(i);
- mAlarmBroadcasts.add(d);
+ mAlarmDeferrals.add(d);
break;
}
}
@@ -201,10 +201,10 @@
// No longer an alarm target, so resume ordinary deferral policy
if (newCount <= 0) {
- final int numEntries = mAlarmBroadcasts.size();
+ final int numEntries = mAlarmDeferrals.size();
for (int i = 0; i < numEntries; i++) {
- if (recipientUid == mAlarmBroadcasts.get(i).uid) {
- Deferrals d = mAlarmBroadcasts.remove(i);
+ if (recipientUid == mAlarmDeferrals.get(i).uid) {
+ Deferrals d = mAlarmDeferrals.remove(i);
insertLocked(mDeferredBroadcasts, d);
break;
}
@@ -234,7 +234,13 @@
// General deferrals not holding up alarms
private final ArrayList<Deferrals> mDeferredBroadcasts = new ArrayList<>();
// Deferrals that *are* holding up alarms; ordered by alarm dispatch time
- private final ArrayList<Deferrals> mAlarmBroadcasts = new ArrayList<>();
+ private final ArrayList<Deferrals> mAlarmDeferrals = new ArrayList<>();
+ // Under the "deliver alarm broadcasts immediately" policy, the queue of
+ // upcoming alarm broadcasts. These are always delivered first - if the
+ // policy is changed on the fly from immediate-alarm-delivery to the previous
+ // in-order-queueing behavior, pending immediate alarm deliveries will drain
+ // and then the behavior settle into the pre-U semantics.
+ private final ArrayList<BroadcastRecord> mAlarmQueue = new ArrayList<>();
// Next outbound broadcast, established by getNextBroadcastLocked()
private BroadcastRecord mCurrentBroadcast;
@@ -528,8 +534,9 @@
synchronized (mLock) {
return mCurrentBroadcast == null
&& mOrderedBroadcasts.isEmpty()
+ && mAlarmQueue.isEmpty()
&& isDeferralsListEmpty(mDeferredBroadcasts)
- && isDeferralsListEmpty(mAlarmBroadcasts);
+ && isDeferralsListEmpty(mAlarmDeferrals);
}
}
@@ -557,7 +564,13 @@
}
sb.append(mOrderedBroadcasts.size());
sb.append(" ordered");
- int n = pendingInDeferralsList(mAlarmBroadcasts);
+ int n = mAlarmQueue.size();
+ if (n > 0) {
+ sb.append(", ");
+ sb.append(n);
+ sb.append(" alarms");
+ }
+ n = pendingInDeferralsList(mAlarmDeferrals);
if (n > 0) {
sb.append(", ");
sb.append(n);
@@ -592,8 +605,16 @@
// ----------------------------------
// BroadcastQueue operation support
void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+ final ArrayList<BroadcastRecord> queue =
+ (r.alarm && mQueue.mService.mConstants.mPrioritizeAlarmBroadcasts)
+ ? mAlarmQueue
+ : mOrderedBroadcasts;
+
if (r.receivers == null || r.receivers.isEmpty()) {
- mOrderedBroadcasts.add(r);
+ // Fast no-op path for broadcasts that won't actually be dispatched to
+ // receivers - we still need to handle completion callbacks and historical
+ // records, but we don't need to consider the fancy cases.
+ queue.add(r);
return;
}
@@ -622,7 +643,8 @@
return;
}
} else {
- mOrderedBroadcasts.add(r);
+ // Ordinary broadcast, so put it on the appropriate queue and carry on
+ queue.add(r);
}
}
@@ -652,10 +674,13 @@
BroadcastRecord replaceBroadcastLocked(BroadcastRecord r, String typeForLogging) {
// Simple case, in the ordinary queue.
BroadcastRecord old = replaceBroadcastLocked(mOrderedBroadcasts, r, typeForLogging);
-
+ // ... or possibly in the simple alarm queue
+ if (old == null) {
+ old = replaceBroadcastLocked(mAlarmQueue, r, typeForLogging);
+ }
// If we didn't find it, less-simple: in a deferral queue?
if (old == null) {
- old = replaceDeferredBroadcastLocked(mAlarmBroadcasts, r, typeForLogging);
+ old = replaceDeferredBroadcastLocked(mAlarmDeferrals, r, typeForLogging);
}
if (old == null) {
old = replaceDeferredBroadcastLocked(mDeferredBroadcasts, r, typeForLogging);
@@ -706,6 +731,10 @@
boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
packageName, filterByClasses, userId, doit);
if (doit || !didSomething) {
+ didSomething = cleanupBroadcastListDisabledReceiversLocked(mAlarmQueue,
+ packageName, filterByClasses, userId, doit);
+ }
+ if (doit || !didSomething) {
ArrayList<BroadcastRecord> lockedBootCompletedBroadcasts = new ArrayList<>();
for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
SparseArray<BroadcastRecord> brs =
@@ -731,7 +760,7 @@
packageName, filterByClasses, userId, doit);
}
if (doit || !didSomething) {
- didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts,
+ didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmDeferrals,
packageName, filterByClasses, userId, doit);
}
if (doit || !didSomething) {
@@ -781,12 +810,15 @@
if (mCurrentBroadcast != null) {
mCurrentBroadcast.dumpDebug(proto, fieldId);
}
- for (Deferrals d : mAlarmBroadcasts) {
+ for (Deferrals d : mAlarmDeferrals) {
d.dumpDebug(proto, fieldId);
}
for (BroadcastRecord br : mOrderedBroadcasts) {
br.dumpDebug(proto, fieldId);
}
+ for (BroadcastRecord br : mAlarmQueue) {
+ br.dumpDebug(proto, fieldId);
+ }
for (Deferrals d : mDeferredBroadcasts) {
d.dumpDebug(proto, fieldId);
}
@@ -816,23 +848,33 @@
return mCurrentBroadcast;
}
- final boolean someQueued = !mOrderedBroadcasts.isEmpty();
-
BroadcastRecord next = null;
+ // Alarms in flight take precedence over everything else. This queue
+ // will be non-empty only when the relevant policy is in force, but if
+ // policy has changed on the fly we still need to drain this before we
+ // settle into the legacy behavior.
+ if (!mAlarmQueue.isEmpty()) {
+ next = mAlarmQueue.remove(0);
+ }
+
+ // Next in precedence are deferred BOOT_COMPLETED broadcasts
if (next == null) {
next = dequeueDeferredBootCompletedBroadcast();
}
- if (next == null && !mAlarmBroadcasts.isEmpty()) {
- next = popLocked(mAlarmBroadcasts);
+ // Alarm-related deferrals are next in precedence...
+ if (next == null && !mAlarmDeferrals.isEmpty()) {
+ next = popLocked(mAlarmDeferrals);
if (DEBUG_BROADCAST_DEFERRAL && next != null) {
Slog.i(TAG, "Next broadcast from alarm targets: " + next);
}
}
+ final boolean someQueued = !mOrderedBroadcasts.isEmpty();
+
if (next == null && !mDeferredBroadcasts.isEmpty()) {
- // We're going to deliver either:
+ // A this point we're going to deliver either:
// 1. the next "overdue" deferral; or
// 2. the next ordinary ordered broadcast; *or*
// 3. the next not-yet-overdue deferral.
@@ -937,7 +979,7 @@
scheduleDeferralCheckLocked(true);
} else {
// alarm-related: strict order-encountered
- mAlarmBroadcasts.add(d);
+ mAlarmDeferrals.add(d);
}
} else {
// We're already deferring, but something was slow again. Reset the
@@ -999,7 +1041,7 @@
* immediately deliverable. Used by the wait-for-broadcast-idle mechanism.
*/
public void cancelDeferralsLocked() {
- zeroDeferralTimes(mAlarmBroadcasts);
+ zeroDeferralTimes(mAlarmDeferrals);
zeroDeferralTimes(mDeferredBroadcasts);
}
@@ -1023,7 +1065,7 @@
Deferrals d = findUidLocked(uid, mDeferredBroadcasts);
// ...but if not there, also check alarm-prioritized deferrals
if (d == null) {
- d = findUidLocked(uid, mAlarmBroadcasts);
+ d = findUidLocked(uid, mAlarmDeferrals);
}
return d;
}
@@ -1035,7 +1077,7 @@
private boolean removeDeferral(Deferrals d) {
boolean didRemove = mDeferredBroadcasts.remove(d);
if (!didRemove) {
- didRemove = mAlarmBroadcasts.remove(d);
+ didRemove = mAlarmDeferrals.remove(d);
}
return didRemove;
}
@@ -1103,14 +1145,20 @@
} else {
pw.println(" (null)");
}
+ printed |= dumper.didPrint();
- dumper.setHeading("Active ordered broadcasts");
- dumper.setLabel("Active Ordered Broadcast");
- for (Deferrals d : mAlarmBroadcasts) {
- d.dumpLocked(dumper);
+ dumper.setHeading("Active alarm broadcasts");
+ dumper.setLabel("Active Alarm Broadcast");
+ for (BroadcastRecord br : mAlarmQueue) {
+ dumper.dump(br);
}
printed |= dumper.didPrint();
+ dumper.setHeading("Active ordered broadcasts");
+ dumper.setLabel("Active Ordered Broadcast");
+ for (Deferrals d : mAlarmDeferrals) {
+ d.dumpLocked(dumper);
+ }
for (BroadcastRecord br : mOrderedBroadcasts) {
dumper.dump(br);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 9be22c0..752c6b6 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Intent;
@@ -24,8 +26,11 @@
import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Objects;
import java.util.Set;
/**
@@ -34,22 +39,23 @@
public abstract class BroadcastQueue {
public static final String TAG = "BroadcastQueue";
- final ActivityManagerService mService;
- final Handler mHandler;
- final BroadcastConstants mConstants;
- final BroadcastSkipPolicy mSkipPolicy;
- final String mQueueName;
+ final @NonNull ActivityManagerService mService;
+ final @NonNull Handler mHandler;
+ final @NonNull BroadcastConstants mConstants;
+ final @NonNull BroadcastSkipPolicy mSkipPolicy;
+ final @NonNull String mQueueName;
- BroadcastQueue(ActivityManagerService service, Handler handler,
- String name, BroadcastConstants constants) {
- mService = service;
- mHandler = handler;
- mQueueName = name;
- mConstants = constants;
- mSkipPolicy = new BroadcastSkipPolicy(service);
+ BroadcastQueue(@NonNull ActivityManagerService service, @NonNull Handler handler,
+ @NonNull String name, @NonNull BroadcastConstants constants,
+ @NonNull BroadcastSkipPolicy skipPolicy) {
+ mService = Objects.requireNonNull(service);
+ mHandler = Objects.requireNonNull(handler);
+ mQueueName = Objects.requireNonNull(name);
+ mConstants = Objects.requireNonNull(constants);
+ mSkipPolicy = Objects.requireNonNull(skipPolicy);
}
- void start(ContentResolver resolver) {
+ void start(@NonNull ContentResolver resolver) {
mConstants.startObserving(mHandler, resolver);
}
@@ -60,9 +66,11 @@
public abstract boolean isDelayBehindServices();
- public abstract BroadcastRecord getPendingBroadcastLocked();
+ @GuardedBy("mService")
+ public abstract @Nullable BroadcastRecord getPendingBroadcastLocked();
- public abstract BroadcastRecord getActiveBroadcastLocked();
+ @GuardedBy("mService")
+ public abstract @Nullable BroadcastRecord getActiveBroadcastLocked();
/**
* Enqueue the given broadcast to be eventually dispatched.
@@ -73,9 +81,8 @@
* When {@link Intent#FLAG_RECEIVER_REPLACE_PENDING} is set, this method
* internally handles replacement of any matching broadcasts.
*/
- public abstract void enqueueBroadcastLocked(BroadcastRecord r);
-
- public abstract BroadcastRecord getMatchingOrderedReceiver(IBinder receiver);
+ @GuardedBy("mService")
+ public abstract void enqueueBroadcastLocked(@NonNull BroadcastRecord r);
/**
* Signal delivered back from a {@link BroadcastReceiver} to indicate that
@@ -83,44 +90,55 @@
* <p>
* If this signal isn't delivered back in a timely fashion, we assume the
* receiver has somehow wedged and we trigger an ANR.
+ *
+ * @param receiver the value to match against
+ * {@link BroadcastRecord#receiver} to identify the caller.
*/
- public abstract boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
- String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices);
+ @GuardedBy("mService")
+ public abstract boolean finishReceiverLocked(@NonNull IBinder receiver, int resultCode,
+ @Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
+ boolean waitForServices);
+ @GuardedBy("mService")
public abstract void backgroundServicesFinishedLocked(int userId);
/**
* Signal from OS internals that the given process has just been actively
* attached, and is ready to begin receiving broadcasts.
*/
- public abstract boolean onApplicationAttachedLocked(ProcessRecord app);
+ @GuardedBy("mService")
+ public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process has timed out during
* an attempted start and attachment.
*/
- public abstract boolean onApplicationTimeoutLocked(ProcessRecord app);
+ @GuardedBy("mService")
+ public abstract boolean onApplicationTimeoutLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process, which had already been
* previously attached, has now encountered a problem such as crashing or
* not responding.
*/
- public abstract boolean onApplicationProblemLocked(ProcessRecord app);
+ @GuardedBy("mService")
+ public abstract boolean onApplicationProblemLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process has been killed, and is
* no longer actively running.
*/
- public abstract boolean onApplicationCleanupLocked(ProcessRecord app);
+ @GuardedBy("mService")
+ public abstract boolean onApplicationCleanupLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given package (or some subset of that
* package) has been disabled or uninstalled, and that any pending
* broadcasts should be cleaned up.
*/
- public abstract boolean cleanupDisabledPackageReceiversLocked(
- String packageName, Set<String> filterByClasses, int userId, boolean doit);
+ @GuardedBy("mService")
+ public abstract boolean cleanupDisabledPackageReceiversLocked(@Nullable String packageName,
+ @Nullable Set<String> filterByClasses, int userId, boolean doit);
/**
* Quickly determine if this queue has broadcasts that are still waiting to
@@ -133,7 +151,7 @@
/**
* Brief summary of internal state, useful for debugging purposes.
*/
- public abstract String describeState();
+ public abstract @NonNull String describeState();
/**
* Flush any broadcasts still waiting to be delivered, causing them to be
@@ -143,8 +161,10 @@
*/
public abstract void flush();
- public abstract void dumpDebug(ProtoOutputStream proto, long fieldId);
+ public abstract void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId);
- public abstract boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, String dumpPackage, boolean needSep);
+ @GuardedBy("mService")
+ public abstract boolean dumpLocked(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+ @NonNull String[] args, int opti, boolean dumpAll, @Nullable String dumpPackage,
+ boolean needSep);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 2b3b211..d914612 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -42,7 +42,6 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.AppGlobals;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.RemoteServiceException.CannotDeliverBroadcastException;
@@ -213,7 +212,14 @@
BroadcastQueueImpl(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
- super(service, handler, name, constants);
+ this(service, handler, name, constants, new BroadcastSkipPolicy(service),
+ allowDelayBehindServices);
+ }
+
+ BroadcastQueueImpl(ActivityManagerService service, Handler handler,
+ String name, BroadcastConstants constants, BroadcastSkipPolicy skipPolicy,
+ boolean allowDelayBehindServices) {
+ super(service, handler, name, constants, skipPolicy);
mHandler = new BroadcastHandler(handler.getLooper());
mDelayBehindServices = allowDelayBehindServices;
mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
@@ -520,10 +526,17 @@
public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
- if (br != null && br.receiver == receiver) {
- return br;
+ if (br == null) {
+ Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
+ + "] no active broadcast");
+ return null;
}
- return null;
+ if (br.receiver != receiver) {
+ Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
+ + "] active broadcast " + br.receiver + " doesn't match " + receiver);
+ return null;
+ }
+ return br;
}
// > 0 only, no worry about "eventual" recycling
@@ -551,6 +564,17 @@
}, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
}
+ public boolean finishReceiverLocked(IBinder receiver, int resultCode,
+ String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
+ final BroadcastRecord r = getMatchingOrderedReceiver(receiver);
+ if (r != null) {
+ return finishReceiverLocked(r, resultCode,
+ resultData, resultExtras, resultAbort, waitForServices);
+ } else {
+ return false;
+ }
+ }
+
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
final int state = r.state;
@@ -1354,9 +1378,8 @@
// Broadcast is being executed, its package can't be stopped.
try {
- AppGlobals.getPackageManager().setPackageStoppedState(
+ mService.mPackageManagerInt.setPackageStoppedState(
r.curComponent.getPackageName(), false, r.userId);
- } catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.curComponent.getPackageName() + ": " + e);
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 9abd01a..78629b0 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -464,10 +464,9 @@
try {
checkTime(startTime,
"getContentProviderImpl: before set stopped state");
- AppGlobals.getPackageManager().setPackageStoppedState(
+ mService.mPackageManagerInt.setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
checkTime(startTime, "getContentProviderImpl: after set stopped state");
- } catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ cpr.appInfo.packageName + ": " + e);
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index baf062d..6087f76 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -19,11 +19,13 @@
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.ArrayMap;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
/** Rate limiter for adding errors into dropbox. */
public class DropboxRateLimiter {
+ private static final String TAG = "DropboxRateLimiter";
// After RATE_LIMIT_ALLOWED_ENTRIES have been collected (for a single breakdown of
// process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has
// elapsed, after which the current count for this breakdown will be reset.
@@ -105,6 +107,15 @@
mLastMapCleanUp = now;
}
+ /** Resets the rate limiter memory. */
+ void reset() {
+ synchronized (mErrorClusterRecords) {
+ mErrorClusterRecords.clear();
+ }
+ mLastMapCleanUp = 0L;
+ Slog.i(TAG, "Rate limiter reset.");
+ }
+
String errorKey(String eventType, String processName) {
return eventType + processName;
}
diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java
index 2498f76..30811a1 100644
--- a/services/core/java/com/android/server/am/HostingRecord.java
+++ b/services/core/java/com/android/server/am/HostingRecord.java
@@ -30,9 +30,10 @@
import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SERVICE;
import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SYSTEM;
import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_TOP_ACTIVITY;
-import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_PUSH_MESSAGE;
-import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_PUSH_MESSAGE_OVER_QUOTA;
import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_JOB;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TYPE__UNKNOWN;
@@ -97,6 +98,7 @@
public static final String TRIGGER_TYPE_ALARM = "alarm";
public static final String TRIGGER_TYPE_PUSH_MESSAGE = "push_message";
public static final String TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA = "push_message_over_quota";
+ public static final String TRIGGER_TYPE_JOB = "job";
@NonNull private final String mHostingType;
private final String mHostingName;
@@ -126,10 +128,11 @@
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName,
- String definingPackageName, int definingUid, String definingProcessName) {
+ String definingPackageName, int definingUid, String definingProcessName,
+ String triggerType) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, definingPackageName,
definingUid, false /* isTopApp */, definingProcessName, null /* action */,
- TRIGGER_TYPE_UNKNOWN);
+ triggerType);
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName, boolean isTopApp) {
@@ -313,9 +316,11 @@
case TRIGGER_TYPE_ALARM:
return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM;
case TRIGGER_TYPE_PUSH_MESSAGE:
- return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_PUSH_MESSAGE;
+ return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE;
case TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA:
- return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_PUSH_MESSAGE_OVER_QUOTA;
+ return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
+ case TRIGGER_TYPE_JOB:
+ return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_JOB;
default:
return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ccbca76..48eb0de 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -133,7 +133,6 @@
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ProcessChangeItem;
import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -1790,14 +1789,6 @@
if (app.info.isEmbeddedDexUsed()) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
- } else if (app.info.isPrivilegedApp()) {
- final PackageList pkgList = app.getPkgList();
- synchronized (pkgList) {
- if (DexManager.isPackageSelectedToRunOob(
- pkgList.getPackageListLocked().keySet())) {
- runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
- }
- }
}
if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
index de70fa0..dcadd5f 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -29,6 +29,7 @@
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.IAmbientContextObserver;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -53,6 +54,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
/**
* Per-user manager service for {@link AmbientContextEvent}s.
@@ -165,20 +168,17 @@
* package. A new registration from the same package will overwrite the previous registration.
*/
public void onRegisterObserver(AmbientContextEventRequest request,
- PendingIntent pendingIntent, RemoteCallback clientStatusCallback) {
+ String packageName, IAmbientContextObserver observer) {
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
- sendStatusCallback(
- clientStatusCallback,
- AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
+ completeRegistration(observer, AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
// Register package and add to existing ClientRequests cache
- startDetection(request, pendingIntent.getCreatorPackage(),
- createDetectionResultRemoteCallback(), clientStatusCallback);
- mMaster.newClientAdded(mUserId, request, pendingIntent, clientStatusCallback);
+ startDetection(request, packageName, observer);
+ mMaster.newClientAdded(mUserId, request, packageName, observer);
}
}
@@ -186,49 +186,46 @@
* Returns a RemoteCallback that handles the status from the detection service, and
* sends results to the client callback.
*/
- private RemoteCallback getServerStatusCallback(RemoteCallback clientStatusCallback) {
+ private RemoteCallback getServerStatusCallback(Consumer<Integer> statusConsumer) {
return new RemoteCallback(result -> {
AmbientContextDetectionServiceStatus serviceStatus =
(AmbientContextDetectionServiceStatus) result.get(
AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY);
final long token = Binder.clearCallingIdentity();
try {
- String packageName = serviceStatus.getPackageName();
- Bundle bundle = new Bundle();
- bundle.putInt(
- AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
- serviceStatus.getStatusCode());
- clientStatusCallback.sendResult(bundle);
int statusCode = serviceStatus.getStatusCode();
+ statusConsumer.accept(statusCode);
Slog.i(TAG, "Got detection status of " + statusCode
- + " for " + packageName);
+ + " for " + serviceStatus.getPackageName());
} finally {
Binder.restoreCallingIdentity(token);
}
});
}
- @VisibleForTesting
void startDetection(AmbientContextEventRequest request, String callingPackage,
- RemoteCallback detectionResultCallback, RemoteCallback clientStatusCallback) {
+ IAmbientContextObserver observer) {
Slog.d(TAG, "Requested detection of " + request.getEventTypes());
synchronized (mLock) {
if (setUpServiceIfNeeded()) {
ensureRemoteServiceInitiated();
- mRemoteService.startDetection(request, callingPackage, detectionResultCallback,
- getServerStatusCallback(clientStatusCallback));
+ mRemoteService.startDetection(request, callingPackage,
+ createDetectionResultRemoteCallback(),
+ getServerStatusCallback(
+ statusCode -> completeRegistration(observer, statusCode)));
} else {
Slog.w(TAG, "No valid component found for AmbientContextDetectionService");
- sendStatusToCallback(clientStatusCallback,
- AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
+ completeRegistration(observer,
+ AmbientContextManager.STATUS_NOT_SUPPORTED);
}
}
}
/**
- * Sends an intent with a status code and empty events.
+ * Sends the result response with the specified status to the callback.
*/
- void sendStatusCallback(RemoteCallback statusCallback, int statusCode) {
+ static void sendStatusCallback(RemoteCallback statusCallback,
+ @AmbientContextManager.StatusCode int statusCode) {
Bundle bundle = new Bundle();
bundle.putInt(
AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
@@ -236,6 +233,15 @@
statusCallback.sendResult(bundle);
}
+ static void completeRegistration(IAmbientContextObserver observer, int statusCode) {
+ try {
+ observer.onRegistrationComplete(statusCode);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IAmbientContextObserver.onRegistrationComplete: "
+ + e.getMessage());
+ }
+ }
+
/**
* Unregisters the client from all previously registered events by removing from the
* mExistingRequests map, and unregister events from the service if those events are not
@@ -255,7 +261,7 @@
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
- sendStatusToCallback(statusCallback,
+ sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_NOT_SUPPORTED);
return;
}
@@ -263,7 +269,8 @@
mRemoteService.queryServiceStatus(
eventTypes,
callingPackage,
- getServerStatusCallback(statusCallback));
+ getServerStatusCallback(
+ statusCode -> sendStatusCallback(statusCallback, statusCode)));
}
}
@@ -350,18 +357,6 @@
return ComponentName.unflattenFromString(consentComponent);
}
- /**
- * Sends the result response with the specified status to the callback.
- */
- void sendStatusToCallback(RemoteCallback callback,
- @AmbientContextManager.StatusCode int status) {
- Bundle bundle = new Bundle();
- bundle.putInt(
- AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
- status);
- callback.sendResult(bundle);
- }
-
@VisibleForTesting
void stopDetection(String packageName) {
Slog.d(TAG, "Stop detection for " + packageName);
@@ -377,13 +372,13 @@
* Sends out the Intent to the client after the event is detected.
*
* @param pendingIntent Client's PendingIntent for callback
- * @param result result from the detection service
+ * @param events detected events from the detection service
*/
- private void sendDetectionResultIntent(PendingIntent pendingIntent,
- AmbientContextDetectionResult result) {
+ void sendDetectionResultIntent(PendingIntent pendingIntent,
+ List<AmbientContextEvent> events) {
Intent intent = new Intent();
intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS,
- new ArrayList(result.getEvents()));
+ new ArrayList(events));
// Explicitly disallow the receiver from starting activities, to prevent apps from utilizing
// the PendingIntent as a backdoor to do this.
BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -392,7 +387,7 @@
pendingIntent.send(getContext(), 0, intent, null, null, null,
options.toBundle());
Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": "
- + result);
+ + events);
} catch (PendingIntent.CanceledException e) {
Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent);
}
@@ -405,16 +400,19 @@
(AmbientContextDetectionResult) result.get(
AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY);
String packageName = detectionResult.getPackageName();
- PendingIntent pendingIntent = mMaster.getPendingIntent(mUserId, packageName);
- if (pendingIntent == null) {
+ IAmbientContextObserver observer = mMaster.getClientRequestObserver(
+ mUserId, packageName);
+ if (observer == null) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
- sendDetectionResultIntent(pendingIntent, detectionResult);
+ observer.onEvents(detectionResult.getEvents());
Slog.i(TAG, "Got detection result of " + detectionResult.getEvents()
+ " for " + packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IAmbientContextObserver.onEvents: " + e.getMessage());
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
index 4206262..e205e84 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -27,10 +27,12 @@
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextManager;
import android.app.ambientcontext.IAmbientContextManager;
+import android.app.ambientcontext.IAmbientContextObserver;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.RemoteCallback;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
@@ -48,6 +50,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -67,31 +70,27 @@
static class ClientRequest {
private final int mUserId;
private final AmbientContextEventRequest mRequest;
- private final PendingIntent mPendingIntent;
- private final RemoteCallback mClientStatusCallback;
+ private final String mPackageName;
+ private final IAmbientContextObserver mObserver;
ClientRequest(int userId, AmbientContextEventRequest request,
- PendingIntent pendingIntent, RemoteCallback clientStatusCallback) {
+ String packageName, IAmbientContextObserver observer) {
this.mUserId = userId;
this.mRequest = request;
- this.mPendingIntent = pendingIntent;
- this.mClientStatusCallback = clientStatusCallback;
+ this.mPackageName = packageName;
+ this.mObserver = observer;
}
String getPackageName() {
- return mPendingIntent.getCreatorPackage();
+ return mPackageName;
}
AmbientContextEventRequest getRequest() {
return mRequest;
}
- PendingIntent getPendingIntent() {
- return mPendingIntent;
- }
-
- RemoteCallback getClientStatusCallback() {
- return mClientStatusCallback;
+ IAmbientContextObserver getObserver() {
+ return mObserver;
}
boolean hasUserId(int userId) {
@@ -139,16 +138,16 @@
}
void newClientAdded(int userId, AmbientContextEventRequest request,
- PendingIntent pendingIntent, RemoteCallback clientStatusCallback) {
- Slog.d(TAG, "New client added: " + pendingIntent.getCreatorPackage());
+ String callingPackage, IAmbientContextObserver observer) {
+ Slog.d(TAG, "New client added: " + callingPackage);
// Remove any existing ClientRequest for this user and package.
mExistingClientRequests.removeAll(
- findExistingRequests(userId, pendingIntent.getCreatorPackage()));
+ findExistingRequests(userId, callingPackage));
// Add to existing ClientRequests
mExistingClientRequests.add(
- new ClientRequest(userId, request, pendingIntent, clientStatusCallback));
+ new ClientRequest(userId, request, callingPackage, observer));
}
void clientRemoved(int userId, String packageName) {
@@ -167,10 +166,10 @@
}
@Nullable
- PendingIntent getPendingIntent(int userId, String packageName) {
+ IAmbientContextObserver getClientRequestObserver(int userId, String packageName) {
for (ClientRequest clientRequest : mExistingClientRequests) {
if (clientRequest.hasUserIdAndPackageName(userId, packageName)) {
- return clientRequest.getPendingIntent();
+ return clientRequest.getObserver();
}
}
return null;
@@ -236,15 +235,13 @@
* Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
*/
void startDetection(@UserIdInt int userId, AmbientContextEventRequest request,
- String packageName, RemoteCallback detectionResultCallback,
- RemoteCallback statusCallback) {
+ String packageName, IAmbientContextObserver observer) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
if (service != null) {
- service.startDetection(request, packageName, detectionResultCallback,
- statusCallback);
+ service.startDetection(request, packageName, observer);
} else {
Slog.i(TAG, "service not available for user_id: " + userId);
}
@@ -297,8 +294,7 @@
Slog.d(TAG, "Restoring detection for " + clientRequest.getPackageName());
service.startDetection(clientRequest.getRequest(),
clientRequest.getPackageName(),
- service.createDetectionResultRemoteCallback(),
- clientRequest.getClientStatusCallback());
+ clientRequest.getObserver());
}
}
}
@@ -328,16 +324,45 @@
Objects.requireNonNull(request);
Objects.requireNonNull(resultPendingIntent);
Objects.requireNonNull(statusCallback);
+ // Wrap the PendingIntent and statusCallback in a IAmbientContextObserver to make the
+ // code unified
+ IAmbientContextObserver observer = new IAmbientContextObserver.Stub() {
+ @Override
+ public void onEvents(List<AmbientContextEvent> events) throws RemoteException {
+ mService.sendDetectionResultIntent(resultPendingIntent, events);
+ }
+
+ @Override
+ public void onRegistrationComplete(int statusCode) throws RemoteException {
+ AmbientContextManagerPerUserService.sendStatusCallback(statusCallback,
+ statusCode);
+ }
+ };
+ registerObserverWithCallback(request, resultPendingIntent.getCreatorPackage(),
+ observer);
+ }
+
+ /**
+ * Register an observer for Ambient Context events.
+ */
+ @Override
+ public void registerObserverWithCallback(AmbientContextEventRequest request,
+ String packageName,
+ IAmbientContextObserver observer) {
+ Slog.i(TAG, "AmbientContextManagerService registerObserverWithCallback.");
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(observer);
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
- assertCalledByPackageOwner(resultPendingIntent.getCreatorPackage());
+ assertCalledByPackageOwner(packageName);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available.");
- mService.sendStatusCallback(statusCallback,
+ AmbientContextManagerPerUserService.completeRegistration(observer,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
- mService.onRegisterObserver(request, resultPendingIntent, statusCallback);
+ mService.onRegisterObserver(request, packageName, observer);
}
@Override
@@ -359,7 +384,7 @@
assertCalledByPackageOwner(callingPackage);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Detection service not available.");
- mService.sendStatusToCallback(statusCallback,
+ AmbientContextManagerPerUserService.sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
index ec6c2f0..a3ffcde8 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
@@ -22,13 +22,15 @@
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.IAmbientContextObserver;
import android.content.ComponentName;
import android.os.Binder;
import android.os.RemoteCallback;
+import android.os.RemoteException;
import android.os.ShellCommand;
-import android.service.ambientcontext.AmbientContextDetectionResult;
import java.io.PrintWriter;
+import java.util.List;
/**
* Shell command for {@link AmbientContextManagerService}.
@@ -39,6 +41,7 @@
new AmbientContextEventRequest.Builder()
.addEventType(AmbientContextEvent.EVENT_COUGH)
.addEventType(AmbientContextEvent.EVENT_SNORE)
+ .addEventType(AmbientContextEvent.EVENT_BACK_DOUBLE_TAP)
.build();
@NonNull
@@ -50,11 +53,11 @@
/** Callbacks for AmbientContextEventService results used internally for testing. */
static class TestableCallbackInternal {
- private AmbientContextDetectionResult mLastResult;
+ private List<AmbientContextEvent> mLastEvents;
private int mLastStatus;
- public AmbientContextDetectionResult getLastResult() {
- return mLastResult;
+ public List<AmbientContextEvent> getLastEvents() {
+ return mLastEvents;
}
public int getLastStatus() {
@@ -62,19 +65,19 @@
}
@NonNull
- private RemoteCallback createRemoteDetectionResultCallback() {
- return new RemoteCallback(result -> {
- AmbientContextDetectionResult detectionResult =
- (AmbientContextDetectionResult) result.get(
- AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY);
- final long token = Binder.clearCallingIdentity();
- try {
- mLastResult = detectionResult;
- out.println("Detection result available: " + detectionResult);
- } finally {
- Binder.restoreCallingIdentity(token);
+ private IAmbientContextObserver createAmbientContextObserver() {
+ return new IAmbientContextObserver.Stub() {
+ @Override
+ public void onEvents(List<AmbientContextEvent> events) throws RemoteException {
+ mLastEvents = events;
+ out.println("Detection events available: " + events);
}
- });
+
+ @Override
+ public void onRegistrationComplete(int statusCode) throws RemoteException {
+ mLastStatus = statusCode;
+ }
+ };
}
@NonNull
@@ -123,8 +126,7 @@
final String packageName = getNextArgRequired();
mService.startDetection(
userId, REQUEST, packageName,
- sTestableCallbackInternal.createRemoteDetectionResultCallback(),
- sTestableCallbackInternal.createRemoteStatusCallback());
+ sTestableCallbackInternal.createAmbientContextObserver());
return 0;
}
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 158092f..dd0c4b86 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -816,8 +816,7 @@
}
} catch (Throwable t) {
- Slog.e(TAG, "Error while cleaning timeline files: " + t.getMessage() + " "
- + t.getStackTrace());
+ Slog.e(TAG, "Error while cleaning timeline files: ", t);
}
}
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 66a4527..4adbfaa 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -53,8 +53,6 @@
private static final String TAG = "AS.BtHelper";
- private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
-
private final @NonNull AudioDeviceBroker mDeviceBroker;
BtHelper(@NonNull AudioDeviceBroker broker) {
@@ -913,7 +911,7 @@
return "ENCODING_APTX_HD";
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
return "ENCODING_LDAC";
- case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS:
return "ENCODING_OPUS";
default:
return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
diff --git a/services/core/java/com/android/server/backup/BackupUtils.java b/services/core/java/com/android/server/backup/BackupUtils.java
index 96c5621..76eba16 100644
--- a/services/core/java/com/android/server/backup/BackupUtils.java
+++ b/services/core/java/com/android/server/backup/BackupUtils.java
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
public class BackupUtils {
private static final String TAG = "BackupUtils";
@@ -64,8 +65,9 @@
}
if (DEBUG) {
- Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
- + " device=" + signingInfo.getApkContentsSigners());
+ Slog.v(TAG, "signaturesMatch(): stored="
+ + storedSigHashes.stream().map(Arrays::toString).collect(Collectors.toList())
+ + " device=" + Arrays.toString(signingInfo.getApkContentsSigners()));
}
final int nStored = storedSigHashes.size();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 689ddd2..c29755a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -72,7 +72,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
-import com.android.server.biometrics.sensors.CoexCoordinator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -951,16 +950,6 @@
return new ArrayList<>();
}
- public boolean isAdvancedCoexLogicEnabled(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- CoexCoordinator.SETTING_ENABLE_NAME, 1) != 0;
- }
-
- public boolean isCoexFaceNonBypassHapticsDisabled(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- CoexCoordinator.FACE_HAPTIC_DISABLE, 0) != 0;
- }
-
public Supplier<Long> getRequestGenerator() {
final AtomicLong generator = new AtomicLong(0);
return () -> generator.incrementAndGet();
@@ -992,14 +981,6 @@
mEnabledOnKeyguardCallbacks);
mRequestCounter = mInjector.getRequestGenerator();
- // TODO(b/193089985) This logic lives here (outside of CoexCoordinator) so that it doesn't
- // need to depend on context. We can remove this code once the advanced logic is enabled
- // by default.
- CoexCoordinator coexCoordinator = CoexCoordinator.getInstance();
- coexCoordinator.setAdvancedLogicEnabled(injector.isAdvancedCoexLogicEnabled(context));
- coexCoordinator.setFaceHapticDisabledWhenNonBypass(
- injector.isCoexFaceNonBypassHapticsDisabled(context));
-
try {
injector.getActivityManagerService().registerUserSwitchObserver(
new UserSwitchObserver() {
@@ -1333,7 +1314,5 @@
pw.println();
pw.println("CurrentSession: " + mAuthSession);
pw.println();
- pw.println("CoexCoordinator: " + CoexCoordinator.getInstance().toString());
- pw.println();
}
}
diff --git a/services/core/java/com/android/server/biometrics/log/ALSProbe.java b/services/core/java/com/android/server/biometrics/log/ALSProbe.java
new file mode 100644
index 0000000..62f94ed
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/ALSProbe.java
@@ -0,0 +1,163 @@
+/*
+ * 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.server.biometrics.log;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+
+import java.util.concurrent.TimeUnit;
+
+/** Probe for ambient light. */
+final class ALSProbe implements Probe {
+ private static final String TAG = "ALSProbe";
+
+ @Nullable
+ private final SensorManager mSensorManager;
+ @Nullable
+ private final Sensor mLightSensor;
+ @NonNull
+ private final Handler mTimer;
+ @DurationMillisLong
+ private long mMaxSubscriptionTime = -1;
+
+ private boolean mEnabled = false;
+ private boolean mDestroyed = false;
+ private volatile float mLastAmbientLux = -1;
+
+ private final SensorEventListener mLightSensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ mLastAmbientLux = event.values[0];
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ /**
+ * Create a probe with a 1-minute max sampling time.
+ *
+ * @param sensorManager Sensor manager
+ */
+ ALSProbe(@NonNull SensorManager sensorManager) {
+ this(sensorManager, new Handler(Looper.getMainLooper()),
+ TimeUnit.MINUTES.toMillis(1));
+ }
+
+ /**
+ * Create a probe with a given max sampling time.
+ *
+ * Note: The max time is a workaround for potential scheduler bugs where
+ * {@link BaseClientMonitor#destroy()} is not called due to an abnormal lifecycle. Clients
+ * should ensure that {@link #disable()} and {@link #destroy()} are called appropriately and
+ * avoid relying on this timeout to unsubscribe from the sensor when it is not needed.
+ *
+ * @param sensorManager Sensor manager
+ * @param handler Timeout handler
+ * @param maxTime The max amount of time to subscribe to events. If this time is exceeded
+ * {@link #disable()} will be called and no sampling will occur until {@link
+ * #enable()} is called again.
+ */
+ @VisibleForTesting
+ ALSProbe(@Nullable SensorManager sensorManager, @NonNull Handler handler,
+ @DurationMillisLong long maxTime) {
+ mSensorManager = sensorManager;
+ mLightSensor = sensorManager != null
+ ? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null;
+ mTimer = handler;
+ mMaxSubscriptionTime = maxTime;
+
+ if (mSensorManager == null || mLightSensor == null) {
+ Slog.w(TAG, "No sensor - probe disabled");
+ mDestroyed = true;
+ }
+ }
+
+ @Override
+ public synchronized void enable() {
+ if (!mDestroyed) {
+ enableLightSensorLoggingLocked();
+ }
+ }
+
+ @Override
+ public synchronized void disable() {
+ if (!mDestroyed) {
+ disableLightSensorLoggingLocked();
+ }
+ }
+
+ @Override
+ public synchronized void destroy() {
+ disable();
+ mDestroyed = true;
+ }
+
+ /** The most recent lux reading. */
+ public float getCurrentLux() {
+ return mLastAmbientLux;
+ }
+
+ private void enableLightSensorLoggingLocked() {
+ if (!mEnabled) {
+ mEnabled = true;
+ mLastAmbientLux = -1;
+ mSensorManager.registerListener(mLightSensorListener, mLightSensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ Slog.v(TAG, "Enable ALS: " + mLightSensorListener.hashCode());
+ }
+
+ resetTimerLocked(true /* start */);
+ }
+
+ private void disableLightSensorLoggingLocked() {
+ resetTimerLocked(false /* start */);
+
+ if (mEnabled) {
+ mEnabled = false;
+ mLastAmbientLux = -1;
+ mSensorManager.unregisterListener(mLightSensorListener);
+ Slog.v(TAG, "Disable ALS: " + mLightSensorListener.hashCode());
+ }
+ }
+
+ private void resetTimerLocked(boolean start) {
+ mTimer.removeCallbacksAndMessages(this /* token */);
+ if (start && mMaxSubscriptionTime > 0) {
+ mTimer.postDelayed(this::onTimeout, this /* token */, mMaxSubscriptionTime);
+ }
+ }
+
+ private void onTimeout() {
+ Slog.e(TAG, "Max time exceeded for ALS logger - disabling: "
+ + mLightSensorListener.hashCode());
+ disable();
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
index c86a8cb..8265203 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -46,6 +46,9 @@
/** If the display is in AOD. */
boolean isAod();
+ /** If the device is awake or is becoming awake. */
+ boolean isAwake();
+
/**
* Subscribe to context changes.
*
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index 9d2fde7..3d1a634 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -43,7 +43,7 @@
/**
* A default provider for {@link BiometricContext}.
*/
-class BiometricContextProvider implements BiometricContext {
+final class BiometricContextProvider implements BiometricContext {
private static final String TAG = "BiometricContextProvider";
@@ -76,7 +76,8 @@
private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>();
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
- private boolean mIsDozing = false;
+ private boolean mIsAod = false;
+ private boolean mIsAwake = false;
@VisibleForTesting
BiometricContextProvider(@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
@@ -85,9 +86,14 @@
try {
service.setBiometicContextListener(new IBiometricContextListener.Stub() {
@Override
- public void onDozeChanged(boolean isDozing) {
- mIsDozing = isDozing;
- notifyChanged();
+ public void onDozeChanged(boolean isDozing, boolean isAwake) {
+ isDozing = isDozing && isAodEnabled();
+ final boolean changed = (mIsAod != isDozing) || (mIsAwake != isAwake);
+ if (changed) {
+ mIsAod = isDozing;
+ mIsAwake = isAwake;
+ notifyChanged();
+ }
}
private void notifyChanged() {
@@ -97,6 +103,10 @@
notifySubscribers();
}
}
+
+ private boolean isAodEnabled() {
+ return mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
+ }
});
service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() {
@Override
@@ -161,7 +171,12 @@
@Override
public boolean isAod() {
- return mIsDozing && mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
+ return mIsAod;
+ }
+
+ @Override
+ public boolean isAwake() {
+ return mIsAwake;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index 262be08..02b350e 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -17,21 +17,15 @@
package com.android.server.biometrics.log;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
-import android.util.Log;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.Utils;
@@ -43,61 +37,16 @@
public static final String TAG = "BiometricLogger";
public static final boolean DEBUG = false;
- private static final Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static int sAlsCounter;
private final int mStatsModality;
private final int mStatsAction;
private final int mStatsClient;
private final BiometricFrameworkStatsLogger mSink;
- @NonNull private final SensorManager mSensorManager;
+ @NonNull private final ALSProbe mALSProbe;
private long mFirstAcquireTimeMs;
- private boolean mLightSensorEnabled = false;
private boolean mShouldLogMetrics = true;
- private class ALSProbe implements Probe {
- private boolean mDestroyed = false;
-
- @Override
- public synchronized void enable() {
- if (!mDestroyed) {
- setLightSensorLoggingEnabled(getAmbientLightSensor(mSensorManager));
- }
- }
-
- @Override
- public synchronized void disable() {
- if (!mDestroyed) {
- setLightSensorLoggingEnabled(null);
- }
- }
-
- @Override
- public synchronized void destroy() {
- disable();
- mDestroyed = true;
- }
- }
-
- // report only the most recent value
- // consider com.android.server.display.utils.AmbientFilter or similar if need arises
- private volatile float mLastAmbientLux = 0;
-
- private final SensorEventListener mLightSensorListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- mLastAmbientLux = event.values[0];
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // Not used.
- }
- };
-
/** Get a new logger with all unknown fields (for operations that do not require logs). */
public static BiometricLogger ofUnknown(@NonNull Context context) {
return new BiometricLogger(context, BiometricsProtoEnums.MODALITY_UNKNOWN,
@@ -105,6 +54,11 @@
}
/**
+ * Creates a new logger for an instance of a biometric operation.
+ *
+ * Do not reuse across operations. Instead, create a new one or use
+ * {@link #swapAction(Context, int)}.
+ *
* @param context system_server context
* @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants.
* @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants.
@@ -125,7 +79,7 @@
mStatsAction = statsAction;
mStatsClient = statsClient;
mSink = logSink;
- mSensorManager = sensorManager;
+ mALSProbe = new ALSProbe(sensorManager);
}
/** Creates a new logger with the action replaced with the new action. */
@@ -136,6 +90,7 @@
/** Disable logging metrics and only log critical events, such as system health issues. */
public void disableMetrics() {
mShouldLogMetrics = false;
+ mALSProbe.destroy();
}
/** {@link BiometricsProtoEnums} CLIENT_* constants */
@@ -265,7 +220,7 @@
+ ", RequireConfirmation: " + requireConfirmation
+ ", State: " + authState
+ ", Latency: " + latency
- + ", Lux: " + mLastAmbientLux);
+ + ", Lux: " + mALSProbe.getCurrentLux());
} else {
Slog.v(TAG, "Authentication latency: " + latency);
}
@@ -276,7 +231,7 @@
mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
- latency, authState, requireConfirmation, targetUserId, mLastAmbientLux);
+ latency, authState, requireConfirmation, targetUserId, mALSProbe.getCurrentLux());
}
/** Log enrollment outcome. */
@@ -290,7 +245,7 @@
+ ", User: " + targetUserId
+ ", Client: " + mStatsClient
+ ", Latency: " + latency
- + ", Lux: " + mLastAmbientLux
+ + ", Lux: " + mALSProbe.getCurrentLux()
+ ", Success: " + enrollSuccessful);
} else {
Slog.v(TAG, "Enroll latency: " + latency);
@@ -301,7 +256,7 @@
}
mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
- targetUserId, latency, enrollSuccessful, mLastAmbientLux);
+ targetUserId, latency, enrollSuccessful, mALSProbe.getCurrentLux());
}
/** Report unexpected enrollment reported by the HAL. */
@@ -323,7 +278,9 @@
}
/**
- * Get a callback to start/stop ALS capture when a client runs.
+ * Get a callback to start/stop ALS capture when the client runs. Do not create
+ * multiple callbacks since there is at most one light sensor (they will all share
+ * a single probe sampling from that sensor).
*
* If the probe should not run for the entire operation, do not set startWithClient and
* start/stop the problem when needed.
@@ -331,53 +288,7 @@
* @param startWithClient if probe should start automatically when the operation starts.
*/
@NonNull
- public CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
- return new CallbackWithProbe<>(new ALSProbe(), startWithClient);
- }
-
- /** The sensor to use for ALS logging. */
- @Nullable
- protected Sensor getAmbientLightSensor(@NonNull SensorManager sensorManager) {
- return mShouldLogMetrics ? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null;
- }
-
- private void setLightSensorLoggingEnabled(@Nullable Sensor lightSensor) {
- if (DEBUG) {
- Slog.v(TAG, "capturing ambient light using: "
- + (lightSensor != null ? lightSensor : "[disabled]"));
- }
-
- if (lightSensor != null) {
- if (!mLightSensorEnabled) {
- mLightSensorEnabled = true;
- mLastAmbientLux = 0;
- int localAlsCounter;
- synchronized (sLock) {
- localAlsCounter = sAlsCounter++;
- }
-
- if (localAlsCounter == 0) {
- mSensorManager.registerListener(mLightSensorListener, lightSensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- } else {
- Slog.e(TAG, "Ignoring request to subscribe to ALSProbe due to non-zero ALS"
- + " counter: " + localAlsCounter);
- Slog.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
- } else {
- mLightSensorEnabled = false;
- mLastAmbientLux = 0;
- mSensorManager.unregisterListener(mLightSensorListener);
- int localAlsCounter;
- synchronized (sLock) {
- localAlsCounter = --sAlsCounter;
- }
- if (localAlsCounter != 0) {
- Slog.e(TAG, "Non-zero ALS counter after unsubscribing from ALSProbe: "
- + localAlsCounter);
- Slog.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ public CallbackWithProbe<Probe> getAmbientLightProbe(boolean startWithClient) {
+ return new CallbackWithProbe<>(mALSProbe, startWithClient);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 4eb6d38..8a24ff6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -29,7 +29,6 @@
import android.hardware.biometrics.BiometricOverlayConstants;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.security.KeyStore;
import android.util.EventLog;
import android.util.Slog;
@@ -46,9 +45,7 @@
* A class to keep track of the authentication state for a given client.
*/
public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
- implements AuthenticationConsumer {
-
- private static final String TAG = "Biometrics/AuthenticationClient";
+ implements AuthenticationConsumer {
// New, has not started yet
public static final int STATE_NEW = 0;
@@ -67,28 +64,27 @@
STATE_STARTED_PAUSED_ATTEMPTED,
STATE_STOPPED})
@interface State {}
-
+ private static final String TAG = "Biometrics/AuthenticationClient";
+ protected final long mOperationId;
private final boolean mIsStrongBiometric;
private final boolean mRequireConfirmation;
private final ActivityTaskManager mActivityTaskManager;
private final BiometricManager mBiometricManager;
- @Nullable private final TaskStackListener mTaskStackListener;
+ @Nullable
+ private final TaskStackListener mTaskStackListener;
private final LockoutTracker mLockoutTracker;
private final boolean mIsRestricted;
private final boolean mAllowBackgroundAuthentication;
private final boolean mIsKeyguardBypassEnabled;
-
- protected final long mOperationId;
-
+ // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update
+ // the state. We should think of a way to improve this in the future.
+ @State
+ protected int mState = STATE_NEW;
private long mStartTimeMs;
private boolean mAuthAttempted;
private boolean mAuthSuccess = false;
- // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update
- // the state. We should think of a way to improve this in the future.
- protected @State int mState = STATE_NEW;
-
public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int targetUserId, long operationId, boolean restricted, @NonNull String owner,
@@ -111,8 +107,9 @@
mIsKeyguardBypassEnabled = isKeyguardBypassEnabled;
}
- public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
- final @LockoutTracker.LockoutMode int lockoutMode =
+ @LockoutTracker.LockoutMode
+ public int handleFailedAttempt(int userId) {
+ @LockoutTracker.LockoutMode final int lockoutMode =
mLockoutTracker.getLockoutModeForUser(userId);
final PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(getSensorId());
@@ -173,14 +170,16 @@
final ClientMonitorCallbackConverter listener = getListener();
- if (DEBUG) Slog.v(TAG, "onAuthenticated(" + authenticated + ")"
- + ", ID:" + identifier.getBiometricId()
- + ", Owner: " + getOwnerString()
- + ", isBP: " + isBiometricPrompt()
- + ", listener: " + listener
- + ", requireConfirmation: " + mRequireConfirmation
- + ", user: " + getTargetUserId()
- + ", clientMonitor: " + toString());
+ if (DEBUG) {
+ Slog.v(TAG, "onAuthenticated(" + authenticated + ")"
+ + ", ID:" + identifier.getBiometricId()
+ + ", Owner: " + getOwnerString()
+ + ", isBP: " + isBiometricPrompt()
+ + ", listener: " + listener
+ + ", requireConfirmation: " + mRequireConfirmation
+ + ", user: " + getTargetUserId()
+ + ", clientMonitor: " + this);
+ }
final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
if (isCryptoOperation()) {
@@ -239,142 +238,57 @@
getSensorId(), getTargetUserId(), byteToken);
}
- final CoexCoordinator coordinator = CoexCoordinator.getInstance();
- coordinator.onAuthenticationSucceeded(SystemClock.uptimeMillis(), this,
- new CoexCoordinator.Callback() {
- @Override
- public void sendAuthenticationResult(boolean addAuthTokenIfStrong) {
- if (addAuthTokenIfStrong && mIsStrongBiometric) {
- final int result = KeyStore.getInstance().addAuthToken(byteToken);
- Slog.d(TAG, "addAuthToken: " + result);
+ // For BP, BiometricService will add the authToken to Keystore.
+ if (!isBiometricPrompt() && mIsStrongBiometric) {
+ final int result = KeyStore.getInstance().addAuthToken(byteToken);
+ if (result != KeyStore.NO_ERROR) {
+ Slog.d(TAG, "Error adding auth token : " + result);
+ } else {
+ Slog.d(TAG, "addAuthToken: " + result);
+ }
+ } else {
+ Slog.d(TAG, "Skipping addAuthToken");
+ }
+ try {
+ if (listener != null) {
+ if (!mIsRestricted) {
+ listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken,
+ getTargetUserId(), mIsStrongBiometric);
} else {
- Slog.d(TAG, "Skipping addAuthToken");
+ listener.onAuthenticationSucceeded(getSensorId(), null /* identifier */,
+ byteToken,
+ getTargetUserId(), mIsStrongBiometric);
}
-
- if (listener != null) {
- try {
- // Explicitly have if/else here to make it super obvious in case the
- // code is touched in the future.
- if (!mIsRestricted) {
- listener.onAuthenticationSucceeded(getSensorId(),
- identifier,
- byteToken,
- getTargetUserId(),
- mIsStrongBiometric);
- } else {
- listener.onAuthenticationSucceeded(getSensorId(),
- null /* identifier */,
- byteToken,
- getTargetUserId(),
- mIsStrongBiometric);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify listener", e);
- }
- } else {
- Slog.w(TAG, "Client not listening");
- }
+ } else {
+ Slog.e(TAG, "Received successful auth, but client was not listening");
}
-
- @Override
- public void sendHapticFeedback() {
- if (listener != null && mShouldVibrate) {
- vibrateSuccess();
- }
- }
-
- @Override
- public void handleLifecycleAfterAuth() {
- AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */);
- }
-
- @Override
- public void sendAuthenticationCanceled() {
- sendCancelOnly(listener);
- }
- });
- } else { // not authenticated
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify listener", e);
+ mCallback.onClientFinished(this, false);
+ return;
+ }
+ } else {
if (isBackgroundAuth) {
Slog.e(TAG, "cancelling due to background auth");
cancel();
} else {
// Allow system-defined limit of number of attempts before giving up
- final @LockoutTracker.LockoutMode int lockoutMode =
+ @LockoutTracker.LockoutMode final int lockoutMode =
handleFailedAttempt(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
markAlreadyDone();
}
- final CoexCoordinator coordinator = CoexCoordinator.getInstance();
- coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode,
- new CoexCoordinator.Callback() {
- @Override
- public void sendAuthenticationResult(boolean addAuthTokenIfStrong) {
- if (listener != null) {
- try {
- listener.onAuthenticationFailed(getSensorId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify listener", e);
- }
- }
- }
-
- @Override
- public void sendHapticFeedback() {
- if (listener != null && mShouldVibrate) {
- vibrateError();
- }
- }
-
- @Override
- public void handleLifecycleAfterAuth() {
- AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
- }
-
- @Override
- public void sendAuthenticationCanceled() {
- sendCancelOnly(listener);
- }
- });
+ try {
+ listener.onAuthenticationFailed(getSensorId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify listener", e);
+ mCallback.onClientFinished(this, false);
+ return;
+ }
}
}
- }
-
- /**
- * Only call this method on interfaces where lockout does not come from onError, I.E. the
- * old HIDL implementation.
- */
- protected void onLockoutTimed(long durationMillis) {
- final ClientMonitorCallbackConverter listener = getListener();
- final CoexCoordinator coordinator = CoexCoordinator.getInstance();
- coordinator.onAuthenticationError(this, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
- new CoexCoordinator.ErrorCallback() {
- @Override
- public void sendHapticFeedback() {
- if (listener != null && mShouldVibrate) {
- vibrateError();
- }
- }
- });
- }
-
- /**
- * Only call this method on interfaces where lockout does not come from onError, I.E. the
- * old HIDL implementation.
- */
- protected void onLockoutPermanent() {
- final ClientMonitorCallbackConverter listener = getListener();
- final CoexCoordinator coordinator = CoexCoordinator.getInstance();
- coordinator.onAuthenticationError(this,
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
- new CoexCoordinator.ErrorCallback() {
- @Override
- public void sendHapticFeedback() {
- if (listener != null && mShouldVibrate) {
- vibrateError();
- }
- }
- });
+ AuthenticationClient.this.handleLifecycleAfterAuth(authenticated);
}
private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) {
@@ -396,7 +310,7 @@
public void onAcquired(int acquiredInfo, int vendorCode) {
super.onAcquired(acquiredInfo, vendorCode);
- final @LockoutTracker.LockoutMode int lockoutMode =
+ @LockoutTracker.LockoutMode final int lockoutMode =
mLockoutTracker.getLockoutModeForUser(getTargetUserId());
if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
@@ -408,8 +322,6 @@
public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
mState = STATE_STOPPED;
-
- CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError);
}
/**
@@ -419,7 +331,7 @@
public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
- final @LockoutTracker.LockoutMode int lockoutMode =
+ @LockoutTracker.LockoutMode final int lockoutMode =
mLockoutTracker.getLockoutModeForUser(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
@@ -450,22 +362,20 @@
}
/**
- * Handles lifecycle, e.g. {@link BiometricScheduler},
- * {@link com.android.server.biometrics.sensors.BaseClientMonitor.Callback} after authentication
- * results are known. Note that this happens asynchronously from (but shortly after)
- * {@link #onAuthenticated(BiometricAuthenticator.Identifier, boolean, ArrayList)} and allows
- * {@link CoexCoordinator} a chance to invoke/delay this event.
- * @param authenticated
+ * Handles lifecycle, e.g. {@link BiometricScheduler} after authentication. This is necessary
+ * as different clients handle the lifecycle of authentication success/reject differently. I.E.
+ * Fingerprint does not finish authentication when it is rejected.
*/
protected abstract void handleLifecycleAfterAuth(boolean authenticated);
/**
* @return true if a user was detected (i.e. face was found, fingerprint sensor was touched.
- * etc)
+ * etc)
*/
public abstract boolean wasUserDetected();
- public @State int getState() {
+ @State
+ public int getState() {
return mState;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 63609f7..9317c4e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -54,7 +54,7 @@
* interactions with the HAL before finishing.
*
* We currently assume (and require) that each biometric sensor have its own instance of a
- * {@link BiometricScheduler}. See {@link CoexCoordinator}.
+ * {@link BiometricScheduler}.
*/
@MainThread
public class BiometricScheduler {
@@ -156,7 +156,6 @@
private int mTotalOperationsHandled;
private final int mRecentOperationsLimit;
@NonNull private final List<Integer> mRecentOperations;
- @NonNull private final CoexCoordinator mCoexCoordinator;
// Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
@@ -165,11 +164,6 @@
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
Slog.d(getTag(), "[Started] " + clientMonitor);
-
- if (clientMonitor instanceof AuthenticationClient) {
- mCoexCoordinator.addAuthenticationClient(mSensorType,
- (AuthenticationClient<?>) clientMonitor);
- }
}
@Override
@@ -189,10 +183,6 @@
}
Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success);
- if (clientMonitor instanceof AuthenticationClient) {
- mCoexCoordinator.removeAuthenticationClient(mSensorType,
- (AuthenticationClient<?>) clientMonitor);
- }
if (mGestureAvailabilityDispatcher != null) {
mGestureAvailabilityDispatcher.markSensorActive(
@@ -216,8 +206,7 @@
@SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService,
- int recentOperationsLimit,
- @NonNull CoexCoordinator coexCoordinator) {
+ int recentOperationsLimit) {
mBiometricTag = tag;
mHandler = handler;
mSensorType = sensorType;
@@ -227,7 +216,6 @@
mCrashStates = new ArrayDeque<>();
mRecentOperationsLimit = recentOperationsLimit;
mRecentOperations = new ArrayList<>();
- mCoexCoordinator = coexCoordinator;
}
/**
@@ -244,7 +232,7 @@
this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
- LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance());
+ LOG_NUM_RECENT_OPERATIONS);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
deleted file mode 100644
index c8a90e7..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (C) 2021 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.server.biometrics.sensors;
-
-import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_FACE;
-import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_UDFPS;
-import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTypeToString;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.hardware.biometrics.BiometricConstants;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.biometrics.sensors.BiometricScheduler.SensorType;
-import com.android.server.biometrics.sensors.fingerprint.Udfps;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-
-/**
- * Singleton that contains the core logic for determining if haptics and authentication callbacks
- * should be sent to receivers. Note that this class is used even when coex is not required (e.g.
- * single sensor devices, or multi-sensor devices where only a single sensor is authenticating).
- * This allows us to have all business logic in one testable place.
- */
-public class CoexCoordinator {
-
- private static final String TAG = "BiometricCoexCoordinator";
- public static final String SETTING_ENABLE_NAME =
- "com.android.server.biometrics.sensors.CoexCoordinator.enable";
- public static final String FACE_HAPTIC_DISABLE =
- "com.android.server.biometrics.sensors.CoexCoordinator.disable_face_haptics";
- private static final boolean DEBUG = true;
-
- // Successful authentications should be used within this amount of time.
- static final long SUCCESSFUL_AUTH_VALID_DURATION_MS = 5000;
-
- /**
- * Callback interface notifying the owner of "results" from the CoexCoordinator's business
- * logic for accept and reject.
- */
- interface Callback {
- /**
- * Requests the owner to send the result (success/reject) and any associated info to the
- * receiver (e.g. keyguard, BiometricService, etc).
- */
- void sendAuthenticationResult(boolean addAuthTokenIfStrong);
-
- /**
- * Requests the owner to initiate a vibration for this event.
- */
- void sendHapticFeedback();
-
- /**
- * Requests the owner to handle the AuthenticationClient's lifecycle (e.g. finish and remove
- * from scheduler if auth was successful).
- */
- void handleLifecycleAfterAuth();
-
- /**
- * Requests the owner to notify the caller that authentication was canceled.
- */
- void sendAuthenticationCanceled();
- }
-
- /**
- * Callback interface notifying the owner of "results" from the CoexCoordinator's business
- * logic for errors.
- */
- interface ErrorCallback {
- /**
- * Requests the owner to initiate a vibration for this event.
- */
- void sendHapticFeedback();
- }
-
- private static final CoexCoordinator sInstance = new CoexCoordinator();
-
- @VisibleForTesting
- public static class SuccessfulAuth {
- final long mAuthTimestamp;
- final @SensorType int mSensorType;
- final AuthenticationClient<?> mAuthenticationClient;
- final Callback mCallback;
- final CleanupRunnable mCleanupRunnable;
-
- public static class CleanupRunnable implements Runnable {
- @NonNull final LinkedList<SuccessfulAuth> mSuccessfulAuths;
- @NonNull final SuccessfulAuth mAuth;
- @NonNull final Callback mCallback;
-
- public CleanupRunnable(@NonNull LinkedList<SuccessfulAuth> successfulAuths,
- @NonNull SuccessfulAuth auth, @NonNull Callback callback) {
- mSuccessfulAuths = successfulAuths;
- mAuth = auth;
- mCallback = callback;
- }
-
- @Override
- public void run() {
- final boolean removed = mSuccessfulAuths.remove(mAuth);
- Slog.w(TAG, "Removing stale successfulAuth: " + mAuth.toString()
- + ", success: " + removed);
- mCallback.handleLifecycleAfterAuth();
- }
- }
-
- public SuccessfulAuth(@NonNull Handler handler,
- @NonNull LinkedList<SuccessfulAuth> successfulAuths,
- long currentTimeMillis,
- @SensorType int sensorType,
- @NonNull AuthenticationClient<?> authenticationClient,
- @NonNull Callback callback) {
- mAuthTimestamp = currentTimeMillis;
- mSensorType = sensorType;
- mAuthenticationClient = authenticationClient;
- mCallback = callback;
-
- mCleanupRunnable = new CleanupRunnable(successfulAuths, this, callback);
-
- handler.postDelayed(mCleanupRunnable, SUCCESSFUL_AUTH_VALID_DURATION_MS);
- }
-
- @Override
- public String toString() {
- return "SensorType: " + sensorTypeToString(mSensorType)
- + ", mAuthTimestamp: " + mAuthTimestamp
- + ", authenticationClient: " + mAuthenticationClient;
- }
- }
-
- /** The singleton instance. */
- @NonNull
- public static CoexCoordinator getInstance() {
- return sInstance;
- }
-
- @VisibleForTesting
- public void setAdvancedLogicEnabled(boolean enabled) {
- mAdvancedLogicEnabled = enabled;
- }
-
- public void setFaceHapticDisabledWhenNonBypass(boolean disabled) {
- mFaceHapticDisabledWhenNonBypass = disabled;
- }
-
- @VisibleForTesting
- void reset() {
- mClientMap.clear();
- }
-
- // SensorType to AuthenticationClient map
- private final Map<Integer, AuthenticationClient<?>> mClientMap = new HashMap<>();
- @VisibleForTesting final LinkedList<SuccessfulAuth> mSuccessfulAuths = new LinkedList<>();
- private boolean mAdvancedLogicEnabled;
- private boolean mFaceHapticDisabledWhenNonBypass;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
- private CoexCoordinator() {}
-
- public void addAuthenticationClient(@BiometricScheduler.SensorType int sensorType,
- @NonNull AuthenticationClient<?> client) {
- if (DEBUG) {
- Slog.d(TAG, "addAuthenticationClient(" + sensorTypeToString(sensorType) + ")"
- + ", client: " + client);
- }
-
- if (mClientMap.containsKey(sensorType)) {
- Slog.w(TAG, "Overwriting existing client: " + mClientMap.get(sensorType)
- + " with new client: " + client);
- }
-
- mClientMap.put(sensorType, client);
- }
-
- public void removeAuthenticationClient(@BiometricScheduler.SensorType int sensorType,
- @NonNull AuthenticationClient<?> client) {
- if (DEBUG) {
- Slog.d(TAG, "removeAuthenticationClient(" + sensorTypeToString(sensorType) + ")"
- + ", client: " + client);
- }
-
- if (!mClientMap.containsKey(sensorType)) {
- Slog.e(TAG, "sensorType: " + sensorType + " does not exist in map. Client: " + client);
- return;
- }
- mClientMap.remove(sensorType);
- }
-
- /**
- * Notify the coordinator that authentication succeeded (accepted)
- */
- public void onAuthenticationSucceeded(long currentTimeMillis,
- @NonNull AuthenticationClient<?> client,
- @NonNull Callback callback) {
- final boolean isUsingSingleModality = isSingleAuthOnly(client);
-
- if (client.isBiometricPrompt()) {
- if (!isUsingSingleModality && hasMultipleSuccessfulAuthentications()) {
- // only send feedback on the first one
- } else {
- callback.sendHapticFeedback();
- }
- // For BP, BiometricService will add the authToken to Keystore.
- callback.sendAuthenticationResult(false /* addAuthTokenIfStrong */);
- callback.handleLifecycleAfterAuth();
- } else if (isUnknownClient(client)) {
- // Client doesn't exist in our map for some reason. Give the user feedback so the
- // device doesn't feel like it's stuck. All other cases below can assume that the
- // client exists in our map.
- callback.sendHapticFeedback();
- callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- callback.handleLifecycleAfterAuth();
- } else if (mAdvancedLogicEnabled && client.isKeyguard()) {
- if (isUsingSingleModality) {
- // Single sensor authentication
- callback.sendHapticFeedback();
- callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- callback.handleLifecycleAfterAuth();
- } else {
- // Multi sensor authentication
- AuthenticationClient<?> udfps = mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null);
- AuthenticationClient<?> face = mClientMap.getOrDefault(SENSOR_TYPE_FACE, null);
- if (isCurrentFaceAuth(client)) {
- if (isUdfpsActivelyAuthing(udfps)) {
- // Face auth success while UDFPS is actively authing. No callback, no haptic
- // Feedback will be provided after UDFPS result:
- // 1) UDFPS succeeds - simply remove this from the queue
- // 2) UDFPS rejected - use this face auth success to notify clients
- mSuccessfulAuths.add(new SuccessfulAuth(mHandler, mSuccessfulAuths,
- currentTimeMillis, SENSOR_TYPE_FACE, client, callback));
- } else {
- if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
- Slog.w(TAG, "Skipping face success haptic");
- } else {
- callback.sendHapticFeedback();
- }
- callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- callback.handleLifecycleAfterAuth();
- }
- } else if (isCurrentUdfps(client)) {
- if (isFaceScanning()) {
- // UDFPS succeeds while face is still scanning
- // Cancel face auth and/or prevent it from invoking haptics/callbacks after
- face.cancel();
- }
-
- removeAndFinishAllFaceFromQueue();
-
- callback.sendHapticFeedback();
- callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- callback.handleLifecycleAfterAuth();
- } else {
- // Capacitive fingerprint sensor (or other)
- callback.sendHapticFeedback();
- callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- callback.handleLifecycleAfterAuth();
- }
- }
- } else {
- // Non-keyguard authentication. For example, Fingerprint Settings use of
- // FingerprintManager for highlighting fingers
- callback.sendHapticFeedback();
- callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- callback.handleLifecycleAfterAuth();
- }
- }
-
- /**
- * Notify the coordinator that a rejection has occurred.
- */
- public void onAuthenticationRejected(long currentTimeMillis,
- @NonNull AuthenticationClient<?> client,
- @LockoutTracker.LockoutMode int lockoutMode,
- @NonNull Callback callback) {
- final boolean isUsingSingleModality = isSingleAuthOnly(client);
-
- if (mAdvancedLogicEnabled && client.isKeyguard()) {
- if (isUsingSingleModality) {
- callback.sendHapticFeedback();
- callback.handleLifecycleAfterAuth();
- } else {
- // Multi sensor authentication
- AuthenticationClient<?> udfps = mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null);
- AuthenticationClient<?> face = mClientMap.getOrDefault(SENSOR_TYPE_FACE, null);
- if (isCurrentFaceAuth(client)) {
- if (isUdfpsActivelyAuthing(udfps)) {
- // UDFPS should still be running in this case, do not vibrate. However, we
- // should notify the callback and finish the client, so that Keyguard and
- // BiometricScheduler do not get stuck.
- Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps);
- callback.handleLifecycleAfterAuth();
- } else if (isUdfpsAuthAttempted(udfps)) {
- // If UDFPS is STATE_STARTED_PAUSED (e.g. finger rejected but can still
- // auth after pointer goes down, it means UDFPS encountered a rejection. In
- // this case, we need to play the final reject haptic since face auth is
- // also done now.
- callback.sendHapticFeedback();
- callback.handleLifecycleAfterAuth();
- } else {
- // UDFPS auth has never been attempted.
- if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
- Slog.w(TAG, "Skipping face reject haptic");
- } else {
- callback.sendHapticFeedback();
- }
- callback.handleLifecycleAfterAuth();
- }
- } else if (isCurrentUdfps(client)) {
- // Face should either be running, or have already finished
- SuccessfulAuth auth = popSuccessfulFaceAuthIfExists(currentTimeMillis);
- if (auth != null) {
- Slog.d(TAG, "Using recent auth: " + auth);
- callback.handleLifecycleAfterAuth();
-
- auth.mCallback.sendHapticFeedback();
- auth.mCallback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- auth.mCallback.handleLifecycleAfterAuth();
- } else {
- Slog.d(TAG, "UDFPS rejected in multi-sensor auth");
- callback.sendHapticFeedback();
- callback.handleLifecycleAfterAuth();
- }
- } else {
- Slog.d(TAG, "Unknown client rejected: " + client);
- callback.sendHapticFeedback();
- callback.handleLifecycleAfterAuth();
- }
- }
- } else if (client.isBiometricPrompt() && !isUsingSingleModality) {
- if (!isCurrentFaceAuth(client)) {
- callback.sendHapticFeedback();
- }
- callback.handleLifecycleAfterAuth();
- } else {
- callback.sendHapticFeedback();
- callback.handleLifecycleAfterAuth();
- }
-
- // Always notify keyguard, otherwise the cached "running" state in KeyguardUpdateMonitor
- // will get stuck.
- if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
- // Don't send onAuthenticationFailed if we're in lockout, it causes a
- // janky UI on Keyguard/BiometricPrompt since "authentication failed"
- // will show briefly and be replaced by "device locked out" message.
- callback.sendAuthenticationResult(false /* addAuthTokenIfStrong */);
- }
- }
-
- /**
- * Notify the coordinator that an error has occurred.
- */
- public void onAuthenticationError(@NonNull AuthenticationClient<?> client,
- @BiometricConstants.Errors int error, @NonNull ErrorCallback callback) {
- final boolean isUsingSingleModality = isSingleAuthOnly(client);
-
- // Figure out non-coex state
- final boolean shouldUsuallyVibrate;
- if (isCurrentFaceAuth(client)) {
- final boolean notDetectedOnKeyguard = client.isKeyguard() && !client.wasUserDetected();
- final boolean authAttempted = client.wasAuthAttempted();
-
- switch (error) {
- case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
- case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
- case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
- shouldUsuallyVibrate = authAttempted && !notDetectedOnKeyguard;
- break;
- default:
- shouldUsuallyVibrate = false;
- break;
- }
- } else {
- shouldUsuallyVibrate = false;
- }
-
- // Figure out coex state
- final boolean hapticSuppressedByCoex;
- if (mAdvancedLogicEnabled && client.isKeyguard()) {
- if (isUsingSingleModality) {
- hapticSuppressedByCoex = false;
- } else {
- hapticSuppressedByCoex = isCurrentFaceAuth(client)
- && !client.isKeyguardBypassEnabled();
- }
- } else if (client.isBiometricPrompt() && !isUsingSingleModality) {
- hapticSuppressedByCoex = isCurrentFaceAuth(client);
- } else {
- hapticSuppressedByCoex = false;
- }
-
- // Combine and send feedback if appropriate
- if (shouldUsuallyVibrate && !hapticSuppressedByCoex) {
- callback.sendHapticFeedback();
- } else {
- Slog.v(TAG, "no haptic shouldUsuallyVibrate: " + shouldUsuallyVibrate
- + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex);
- }
- }
-
- @Nullable
- private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) {
- for (SuccessfulAuth auth : mSuccessfulAuths) {
- if (currentTimeMillis - auth.mAuthTimestamp >= SUCCESSFUL_AUTH_VALID_DURATION_MS) {
- // TODO(b/193089985): This removes the auth but does not notify the client with
- // an appropriate lifecycle event (such as ERROR_CANCELED), and violates the
- // API contract. However, this might be OK for now since the validity duration
- // is way longer than the time it takes to auth with fingerprint.
- Slog.e(TAG, "Removing stale auth: " + auth);
- mSuccessfulAuths.remove(auth);
- } else if (auth.mSensorType == SENSOR_TYPE_FACE) {
- mSuccessfulAuths.remove(auth);
- return auth;
- }
- }
- return null;
- }
-
- private void removeAndFinishAllFaceFromQueue() {
- // Note that these auth are all successful, but have never notified the client (e.g.
- // keyguard). To comply with the authentication lifecycle, we must notify the client that
- // auth is "done". The safest thing to do is to send ERROR_CANCELED.
- for (SuccessfulAuth auth : mSuccessfulAuths) {
- if (auth.mSensorType == SENSOR_TYPE_FACE) {
- Slog.d(TAG, "Removing from queue, canceling, and finishing: " + auth);
- auth.mCallback.sendAuthenticationCanceled();
- auth.mCallback.handleLifecycleAfterAuth();
- mSuccessfulAuths.remove(auth);
- }
- }
- }
-
- private boolean isCurrentFaceAuth(@NonNull AuthenticationClient<?> client) {
- return client == mClientMap.getOrDefault(SENSOR_TYPE_FACE, null);
- }
-
- private boolean isCurrentUdfps(@NonNull AuthenticationClient<?> client) {
- return client == mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null);
- }
-
- private boolean isFaceScanning() {
- AuthenticationClient<?> client = mClientMap.getOrDefault(SENSOR_TYPE_FACE, null);
- return client != null && client.getState() == AuthenticationClient.STATE_STARTED;
- }
-
- private static boolean isUdfpsActivelyAuthing(@Nullable AuthenticationClient<?> client) {
- if (client instanceof Udfps) {
- return client.getState() == AuthenticationClient.STATE_STARTED;
- }
- return false;
- }
-
- private static boolean isUdfpsAuthAttempted(@Nullable AuthenticationClient<?> client) {
- if (client instanceof Udfps) {
- return client.getState() == AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED;
- }
- return false;
- }
-
- private boolean isUnknownClient(@NonNull AuthenticationClient<?> client) {
- for (AuthenticationClient<?> c : mClientMap.values()) {
- if (c == client) {
- return false;
- }
- }
- return true;
- }
-
- private boolean isSingleAuthOnly(@NonNull AuthenticationClient<?> client) {
- if (mClientMap.values().size() != 1) {
- return false;
- }
-
- for (AuthenticationClient<?> c : mClientMap.values()) {
- if (c != client) {
- return false;
- }
- }
- return true;
- }
-
- private boolean hasMultipleSuccessfulAuthentications() {
- int count = 0;
- for (AuthenticationClient<?> c : mClientMap.values()) {
- if (c.wasAuthSuccessful()) {
- count++;
- }
- if (count > 1) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("Enabled: ").append(mAdvancedLogicEnabled);
- sb.append(", Face Haptic Disabled: ").append(mFaceHapticDisabledWhenNonBypass);
- sb.append(", Queue size: " ).append(mSuccessfulAuths.size());
- for (SuccessfulAuth auth : mSuccessfulAuths) {
- sb.append(", Auth: ").append(auth.toString());
- }
-
- return sb.toString();
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index ae75b7d..a486d16 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -95,10 +95,9 @@
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService,
@NonNull CurrentUserRetriever currentUserRetriever,
- @NonNull UserSwitchCallback userSwitchCallback,
- @NonNull CoexCoordinator coexCoordinator) {
+ @NonNull UserSwitchCallback userSwitchCallback) {
super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService,
- LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
+ LOG_NUM_RECENT_OPERATIONS);
mCurrentUserRetriever = currentUserRetriever;
mUserSwitchCallback = userSwitchCallback;
@@ -112,7 +111,7 @@
this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
- currentUserRetriever, userSwitchCallback, CoexCoordinator.getInstance());
+ currentUserRetriever, userSwitchCallback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index d0c58fd..e18e31ec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -131,7 +131,7 @@
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
- getLogger().createALSCallback(true /* startWithClient */), callback);
+ getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override
@@ -274,7 +274,6 @@
@Override
public void onLockoutTimed(long durationMillis) {
- super.onLockoutTimed(durationMillis);
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
@@ -290,7 +289,6 @@
@Override
public void onLockoutPermanent() {
- super.onLockoutPermanent();
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index da78536..5d62cde 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -115,7 +115,7 @@
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(mPreviewHandleDeleterCallback,
- getLogger().createALSCallback(true /* startWithClient */), callback);
+ getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 1935a5b..9baca98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -101,7 +101,7 @@
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
- getLogger().createALSCallback(true /* startWithClient */), callback);
+ getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 226e458..16d2f7a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -75,7 +75,7 @@
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
- getLogger().createALSCallback(true /* startWithClient */), callback);
+ getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index b530c8d..f7d94c9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -137,7 +137,7 @@
mLockoutCache = lockoutCache;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
- mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
+ mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
mHandler = handler;
mWaitForAuthKeyguard =
@@ -315,21 +315,27 @@
private ICancellationSignal doAuthenticate() throws RemoteException {
final AidlSession session = getFreshDaemon();
+ final OperationContext opContext = getOperationContext();
+ getBiometricContext().subscribe(opContext, ctx -> {
+ if (session.hasContextMethods()) {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ }
+
+ // TODO(b/243836005): this should come via ctx
+ final boolean isAwake = getBiometricContext().isAwake();
+ if (isAwake) {
+ mALSProbeCallback.getProbe().enable();
+ } else {
+ mALSProbeCallback.getProbe().disable();
+ }
+ });
+
if (session.hasContextMethods()) {
- final OperationContext opContext = getOperationContext();
- final ICancellationSignal cancel =
- session.getSession().authenticateWithContext(mOperationId, opContext);
- getBiometricContext()
- .subscribe(
- opContext,
- ctx -> {
- try {
- session.getSession().onContextChanged(ctx);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- });
- return cancel;
+ return session.getSession().authenticateWithContext(mOperationId, opContext);
} else {
return session.getSession().authenticate(mOperationId);
}
@@ -360,7 +366,6 @@
try {
mIsPointerDown = true;
mState = STATE_STARTED;
- mALSProbeCallback.getProbe().enable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
@@ -389,7 +394,6 @@
try {
mIsPointerDown = false;
mState = STATE_STARTED_PAUSED_ATTEMPTED;
- mALSProbeCallback.getProbe().disable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
@@ -424,7 +428,6 @@
@Override
public void onLockoutTimed(long durationMillis) {
- super.onLockoutTimed(durationMillis);
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
@@ -448,7 +451,6 @@
@Override
public void onLockoutPermanent() {
- super.onLockoutPermanent();
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index f4f0a19..e0393b5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -94,7 +94,7 @@
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
- mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
+ mALSProbeCallback = getLogger().getAmbientLightProbe(true /* startWithClient */);
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
@@ -216,7 +216,6 @@
public void onPointerDown(int x, int y, float minor, float major) {
try {
mIsPointerDown = true;
- mALSProbeCallback.getProbe().enable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
@@ -240,7 +239,6 @@
public void onPointerUp() {
try {
mIsPointerDown = false;
- mALSProbeCallback.getProbe().disable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 97fbb5f..7ed1a51 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -86,7 +86,7 @@
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
- mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
+ mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 2a59c8c..5d9af53 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -84,7 +84,7 @@
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
- getLogger().createALSCallback(true /* startWithClient */), callback);
+ getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 25d0752..c835d2f 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -116,8 +116,10 @@
luxLevels = getLuxLevels(resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevelsIdle));
} else {
- brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
- luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux();
+ brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
+ luxLevels = getLuxLevels(resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels));
}
// Display independent, mode independent values
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
new file mode 100644
index 0000000..e9640cf
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -0,0 +1,55 @@
+/*
+ * 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.server.display;
+
+import android.os.IBinder;
+
+import java.util.Objects;
+
+/**
+ * Calls into SurfaceFlinger for Display creation and deletion.
+ */
+public class DisplayControl {
+ private static native IBinder nativeCreateDisplay(String name, boolean secure);
+ private static native void nativeDestroyDisplay(IBinder displayToken);
+
+ /**
+ * Create a display in SurfaceFlinger.
+ *
+ * @param name The name of the display
+ * @param secure Whether this display is secure.
+ * @return The token reference for the display in SurfaceFlinger.
+ */
+ public static IBinder createDisplay(String name, boolean secure) {
+ Objects.requireNonNull(name, "name must not be null");
+ return nativeCreateDisplay(name, secure);
+ }
+
+ /**
+ * Destroy a display in SurfaceFlinger.
+ *
+ * @param displayToken The display token for the display to be destroyed.
+ */
+ public static void destroyDisplay(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ nativeDestroyDisplay(displayToken);
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 3b627ef..4f3fd64 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.os.Environment;
@@ -150,22 +149,12 @@
* </quirks>
*
* <autoBrightness>
- * <brighteningLightDebounceMillis>
+ * <brighteningLightDebounceMillis>
* 2000
- * </brighteningLightDebounceMillis>
+ * </brighteningLightDebounceMillis>
* <darkeningLightDebounceMillis>
* 1000
* </darkeningLightDebounceMillis>
- * <displayBrightnessMapping>
- * <displayBrightnessPoint>
- * <lux>50</lux>
- * <nits>45</nits>
- * </displayBrightnessPoint>
- * <displayBrightnessPoint>
- * <lux>80</lux>
- * <nits>75</nits>
- * </displayBrightnessPoint>
- * </displayBrightnessMapping>
* </autoBrightness>
*
* <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
@@ -279,39 +268,6 @@
// for the corresponding values above
private float[] mBrightness;
-
- /**
- * Array of desired screen brightness in nits corresponding to the lux values
- * in the mBrightnessLevelsLux array. The display brightness is defined as the
- * measured brightness of an all-white image. The brightness values must be non-negative and
- * non-decreasing. This must be overridden in platform specific overlays
- */
- private float[] mBrightnessLevelsNits;
-
- /**
- * Array of light sensor lux values to define our levels for auto backlight
- * brightness support.
- * The N entries of this array define N + 1 control points as follows:
- * (1-based arrays)
- *
- * Point 1: (0, value[1]): lux <= 0
- * Point 2: (level[1], value[2]): 0 < lux <= level[1]
- * Point 3: (level[2], value[3]): level[2] < lux <= level[3]
- * ...
- * Point N+1: (level[N], value[N+1]): level[N] < lux
- *
- * The control points must be strictly increasing. Each control point
- * corresponds to an entry in the brightness backlight values arrays.
- * For example, if lux == level[1] (first element of the levels array)
- * then the brightness will be determined by value[2] (second element
- * of the brightness values array).
- *
- * Spline interpolation is used to determine the auto-brightness
- * backlight values for lux levels between these control points.
- *
- */
- private float[] mBrightnessLevelsLux;
-
private float mBacklightMinimum = Float.NaN;
private float mBacklightMaximum = Float.NaN;
private float mBrightnessDefault = Float.NaN;
@@ -705,20 +661,6 @@
return mAutoBrightnessBrighteningLightDebounce;
}
- /**
- * @return Auto brightness brightening ambient lux levels
- */
- public float[] getAutoBrightnessBrighteningLevelsLux() {
- return mBrightnessLevelsLux;
- }
-
- /**
- * @return Auto brightness brightening nits levels
- */
- public float[] getAutoBrightnessBrighteningLevelsNits() {
- return mBrightnessLevelsNits;
- }
-
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -761,8 +703,6 @@
+ mAutoBrightnessBrighteningLightDebounce
+ ", mAutoBrightnessDarkeningLightDebounce= "
+ mAutoBrightnessDarkeningLightDebounce
- + ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
- + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ "}";
}
@@ -839,7 +779,6 @@
loadBrightnessRampsFromConfigXml();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
- loadAutoBrightnessConfigsFromConfigXml();
mLoadedFrom = "<config.xml>";
}
@@ -1052,7 +991,6 @@
private void loadAutoBrightnessConfigValues(DisplayConfiguration config) {
loadAutoBrightnessBrighteningLightDebounce(config.getAutoBrightness());
loadAutoBrightnessDarkeningLightDebounce(config.getAutoBrightness());
- loadAutoBrightnessDisplayBrightnessMapping(config.getAutoBrightness());
}
/**
@@ -1085,33 +1023,6 @@
}
}
- /**
- * Loads the auto-brightness display brightness mappings. Internally, this takes care of
- * loading the value from the display config, and if not present, falls back to config.xml.
- */
- private void loadAutoBrightnessDisplayBrightnessMapping(AutoBrightness autoBrightnessConfig) {
- if (autoBrightnessConfig == null
- || autoBrightnessConfig.getDisplayBrightnessMapping() == null) {
- mBrightnessLevelsNits = getFloatArray(mContext.getResources()
- .obtainTypedArray(com.android.internal.R.array
- .config_autoBrightnessDisplayValuesNits));
- mBrightnessLevelsLux = getFloatArray(mContext.getResources()
- .obtainTypedArray(com.android.internal.R.array
- .config_autoBrightnessLevels));
- } else {
- final int size = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().size();
- mBrightnessLevelsNits = new float[size];
- mBrightnessLevelsLux = new float[size];
- for (int i = 0; i < size; i++) {
- mBrightnessLevelsNits[i] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getNits().floatValue();
- mBrightnessLevelsLux[i] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getLux().floatValue();
- }
- }
- }
-
private void loadBrightnessMapFromConfigXml() {
// Use the config.xml mapping
final Resources res = mContext.getResources();
@@ -1337,10 +1248,6 @@
com.android.internal.R.string.config_displayLightSensorType);
}
- private void loadAutoBrightnessConfigsFromConfigXml() {
- loadAutoBrightnessDisplayBrightnessMapping(null /*AutoBrightnessConfig*/);
- }
-
private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) {
final SensorDetails sensorDetails = config.getLightSensor();
if (sensorDetails != null) {
@@ -1483,22 +1390,6 @@
}
}
- /**
- * Extracts a float array from the specified {@link TypedArray}.
- *
- * @param array The array to convert.
- * @return the given array as a float array.
- */
- public static float[] getFloatArray(TypedArray array) {
- final int n = array.length();
- float[] vals = new float[n];
- for (int i = 0; i < n; i++) {
- vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT);
- }
- array.recycle();
- return vals;
- }
-
static class SensorData {
public String type;
public String name;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 330379c..b0de844 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -305,7 +305,7 @@
mSurface.release();
mSurface = null;
}
- SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+ DisplayControl.destroyDisplay(getDisplayTokenLocked());
}
@Override
@@ -460,7 +460,7 @@
public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
long presentationDeadlineNanos, int state) {
synchronized (getSyncRoot()) {
- IBinder displayToken = SurfaceControl.createDisplay(mName, mFlags.mSecure);
+ IBinder displayToken = DisplayControl.createDisplay(mName, mFlags.mSecure);
mDevice = new OverlayDisplayDevice(displayToken, mName, mModes, mActiveMode,
DEFAULT_MODE_INDEX, refreshRate, presentationDeadlineNanos,
mFlags, state, surfaceTexture, mNumber) {
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 479629e..38728ce 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -82,8 +82,7 @@
// Called with SyncRoot lock held.
public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener) {
- this(syncRoot, context, handler, listener,
- (String name, boolean secure) -> SurfaceControl.createDisplay(name, secure));
+ this(syncRoot, context, handler, listener, DisplayControl::createDisplay);
}
@VisibleForTesting
@@ -296,7 +295,7 @@
mSurface.release();
mSurface = null;
}
- SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+ DisplayControl.destroyDisplay(getDisplayTokenLocked());
if (mProjection != null && mMediaProjectionCallback != null) {
try {
mProjection.unregisterCallback(mMediaProjectionCallback);
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index d632ee3..146b003 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -388,7 +388,7 @@
String name = display.getFriendlyDisplayName();
String address = display.getDeviceAddress();
- IBinder displayToken = SurfaceControl.createDisplay(name, secure);
+ IBinder displayToken = DisplayControl.createDisplay(name, secure);
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
refreshRate, deviceFlags, address, surface);
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
@@ -621,7 +621,7 @@
mSurface.release();
mSurface = null;
}
- SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+ DisplayControl.destroyDisplay(getDisplayTokenLocked());
}
public void setNameLocked(String name) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 6f890cd..dc1f4dd 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -561,7 +561,7 @@
mAGpsDataConnectionIpAddr = InetAddress.getByAddress(suplIpAddr);
if (DEBUG) Log.d(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
} catch (UnknownHostException e) {
- Log.e(TAG, "Bad IP Address: " + suplIpAddr, e);
+ Log.e(TAG, "Bad IP Address: " + Arrays.toString(suplIpAddr), e);
}
}
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 04cacee..cbbb336 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.logcat;
+import static android.os.Process.getParentPid;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -45,6 +47,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -340,13 +343,6 @@
* access
*/
private String getPackageName(LogAccessRequest request) {
- if (mActivityManagerInternal != null) {
- String packageName = mActivityManagerInternal.getPackageNameByPid(request.mPid);
- if (packageName != null) {
- return packageName;
- }
- }
-
PackageManager pm = mContext.getPackageManager();
if (pm == null) {
// Decline the logd access if PackageManager is null
@@ -355,15 +351,28 @@
}
String[] packageNames = pm.getPackagesForUid(request.mUid);
-
if (ArrayUtils.isEmpty(packageNames)) {
// Decline the logd access if the app name is unknown
Slog.e(TAG, "Unknown calling package name, declining the logd access");
return null;
}
- String firstPackageName = packageNames[0];
+ if (mActivityManagerInternal != null) {
+ int pid = request.mPid;
+ String packageName = mActivityManagerInternal.getPackageNameByPid(pid);
+ while ((packageName == null || !ArrayUtils.contains(packageNames, packageName))
+ && pid != -1) {
+ pid = getParentPid(pid);
+ packageName = mActivityManagerInternal.getPackageNameByPid(pid);
+ }
+ if (packageName != null && ArrayUtils.contains(packageNames, packageName)) {
+ return packageName;
+ }
+ }
+
+ Arrays.sort(packageNames);
+ String firstPackageName = packageNames[0];
if (firstPackageName == null || firstPackageName.isEmpty()) {
// Decline the logd access if the package name from uid is unknown
Slog.e(TAG, "Unknown calling package name, declining the logd access");
diff --git a/services/core/java/com/android/server/logcat/OWNERS b/services/core/java/com/android/server/logcat/OWNERS
index 9588fa9..87d30f3 100644
--- a/services/core/java/com/android/server/logcat/OWNERS
+++ b/services/core/java/com/android/server/logcat/OWNERS
@@ -1,5 +1,7 @@
cbrubaker@google.com
eunjeongshin@google.com
+georgechan@google.com
jsharkey@google.com
vishwath@google.com
wenhaowang@google.com
+xiaozhenl@google.com
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index c12dc8e..9a19031 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -32,7 +32,6 @@
import android.os.PowerWhitelistManager;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.EventLog;
import android.util.Log;
import android.view.KeyEvent;
@@ -118,12 +117,6 @@
int componentType = getComponentType(pendingIntent);
ComponentName componentName = getComponentName(pendingIntent, componentType);
if (componentName != null) {
- if (!TextUtils.equals(componentName.getPackageName(), sessionPackageName)) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
- throw new IllegalArgumentException("ComponentName does not belong to "
- + "sessionPackageName. sessionPackageName = " + sessionPackageName
- + ", ComponentName pkg = " + componentName.getPackageName());
- }
return new MediaButtonReceiverHolder(userId, pendingIntent, componentName,
componentType);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 4f8771a..34cd6a0 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -50,8 +50,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.EventLog;
import android.util.Log;
import android.view.KeyEvent;
@@ -914,7 +912,8 @@
}
@Override
- public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+ public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
+ throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -922,7 +921,7 @@
return;
}
mMediaButtonReceiverHolder =
- MediaButtonReceiverHolder.create(mContext, mUserId, pi, mPackageName);
+ MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally {
Binder.restoreCallingIdentity(token);
@@ -933,15 +932,6 @@
public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
- //mPackageName has been verified in MediaSessionService.enforcePackageName().
- if (receiver != null && !TextUtils.equals(
- mPackageName, receiver.getPackageName())) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
- throw new IllegalArgumentException("receiver does not belong to "
- + "package name provided to MediaSessionRecord. Pkg = " + mPackageName
- + ", Receiver Pkg = " + receiver.getPackageName());
- }
-
if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
!= 0) {
return;
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
index 5599b0c..8ce7b57 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -36,7 +36,6 @@
import android.text.TextUtils;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.HexDump;
@@ -45,8 +44,8 @@
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.GregorianCalendar;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -155,7 +154,7 @@
try {
final String[] packageNames = mPm.getPackagesForUid(uid);
if (packageNames == null || packageNames.length == 0) {
- Slog.e(TAG, "Couldn't find package: " + packageNames);
+ Slog.e(TAG, "Couldn't find package: " + Arrays.toString(packageNames));
return false;
}
ai = mPm.getApplicationInfo(packageNames[0], 0);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 6cfe093..4b6543b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -74,7 +74,6 @@
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.ArtStatsLogUtils;
import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
-import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
@@ -787,10 +786,7 @@
*/
private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
boolean isUsedByOtherApps) {
- // When an app or priv app is configured to run out of box, only verify it.
- if (info.isEmbeddedDexUsed()
- || (info.isPrivilegedApp()
- && DexManager.isPackageSelectedToRunOob(info.packageName))) {
+ if (info.isEmbeddedDexUsed()) {
return "verify";
}
@@ -827,10 +823,7 @@
* handling the case where the package code is used by other apps.
*/
private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter) {
- // When an app or priv app is configured to run out of box, only verify it.
- if (pkg.isUseEmbeddedDex()
- || (pkg.isPrivileged()
- && DexManager.isPackageSelectedToRunOob(pkg.getPackageName()))) {
+ if (pkg.isUseEmbeddedDex()) {
return "verify";
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index e969d93..330e3313 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -727,6 +727,12 @@
return snapshot().checkUidSignaturesForAllUsers(uid1, uid2);
}
+ @Override
+ public void setPackageStoppedState(@NonNull String packageName, boolean stopped,
+ int userId) {
+ mService.setPackageStoppedState(snapshot(), packageName, stopped, userId);
+ }
+
@NonNull
@Override
@Deprecated
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 297439f..8f89fdd 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -321,4 +321,15 @@
* {@link UserManager#isUserVisible()} in the given display.
*/
public abstract boolean isUserVisible(@UserIdInt int userId, int displayId);
+
+ /**
+ * Returns the display id assigned to the user, or {@code Display.INVALID_DISPLAY} if the
+ * user is not assigned to any display.
+ *
+ * <p>The current foreground user is associated with the main display, while other users would
+ * only assigned to a display if they were started with
+ * {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}. If the user is a profile
+ * and is running, it's assigned to its parent display.
+ */
+ public abstract int getDisplayAssignedToUser(@UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d8b7fda..3b898f8 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1731,6 +1731,18 @@
return false;
}
+ // TODO(b/239982558): add unit test
+ private int getDisplayAssignedToUser(@UserIdInt int userId) {
+ if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
+ return Display.DEFAULT_DISPLAY;
+ }
+ synchronized (mUsersLock) {
+ return mUsersOnSecondaryDisplays == null
+ ? Display.INVALID_DISPLAY
+ : mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY);
+ }
+ }
+
private boolean isCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) {
int currentUserId = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
@@ -6695,6 +6707,11 @@
public boolean isUserVisible(int userId, int displayId) {
return isUserVisibleOnDisplay(userId, displayId);
}
+
+ @Override
+ public int getDisplayAssignedToUser(int userId) {
+ return UserManagerService.this.getDisplayAssignedToUser(userId);
+ }
} // class LocalService
/**
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 17109e9..8c2da45 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -36,7 +36,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.util.Log;
@@ -58,8 +57,6 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -81,10 +78,6 @@
private static final String TAG = "DexManager";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob";
- private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST =
- "pm.dexopt.priv-apps-oob-list";
-
// System server cannot load executable code outside system partitions.
// However it can load verification data - thus we pick the "verify" compiler filter.
private static final String SYSTEM_SERVER_COMPILER_FILTER = "verify";
@@ -910,45 +903,6 @@
}
/**
- * Returns whether the given package is in the list of privilaged apps that should run out of
- * box. This only makes sense if the feature is enabled. Note that when the the OOB list is
- * empty, all priv apps will run in OOB mode.
- */
- public static boolean isPackageSelectedToRunOob(String packageName) {
- return isPackageSelectedToRunOob(Arrays.asList(packageName));
- }
-
- /**
- * Returns whether any of the given packages are in the list of privilaged apps that should run
- * out of box. This only makes sense if the feature is enabled. Note that when the the OOB list
- * is empty, all priv apps will run in OOB mode.
- */
- public static boolean isPackageSelectedToRunOob(Collection<String> packageNamesInSameProcess) {
- return isPackageSelectedToRunOobInternal(
- SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false),
- SystemProperties.get(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, "ALL"),
- packageNamesInSameProcess);
- }
-
- @VisibleForTesting
- /* package */ static boolean isPackageSelectedToRunOobInternal(boolean isEnabled,
- String whitelist, Collection<String> packageNamesInSameProcess) {
- if (!isEnabled) {
- return false;
- }
-
- if ("ALL".equals(whitelist)) {
- return true;
- }
- for (String oobPkgName : whitelist.split(",")) {
- if (packageNamesInSameProcess.contains(oobPkgName)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Generates log if the archive located at {@code fileName} has uncompressed dex file that can
* be direclty mapped.
*/
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1a1de03..79fe905 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -185,6 +185,7 @@
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.accessibility.util.AccessibilityUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.logging.MetricsLogger;
@@ -224,6 +225,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
/**
* WindowManagerPolicy implementation for the Android phone UI. This
@@ -437,6 +439,7 @@
}
};
+ private Supplier<GlobalActions> mGlobalActionsFactory;
private GlobalActions mGlobalActions;
private Handler mHandler;
@@ -1544,7 +1547,7 @@
void showGlobalActionsInternal() {
if (mGlobalActions == null) {
- mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
+ mGlobalActions = mGlobalActionsFactory.get();
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
@@ -1868,11 +1871,45 @@
mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
}
+ /** Point of injection for test dependencies. */
+ @VisibleForTesting
+ static class Injector {
+ private final Context mContext;
+ private final WindowManagerFuncs mWindowManagerFuncs;
+
+ Injector(Context context, WindowManagerFuncs funcs) {
+ mContext = context;
+ mWindowManagerFuncs = funcs;
+ }
+
+ Context getContext() {
+ return mContext;
+ }
+
+ WindowManagerFuncs getWindowManagerFuncs() {
+ return mWindowManagerFuncs;
+ }
+
+ AccessibilityShortcutController getAccessibilityShortcutController(
+ Context context, Handler handler, int initialUserId) {
+ return new AccessibilityShortcutController(context, handler, initialUserId);
+ }
+
+ Supplier<GlobalActions> getGlobalActionsFactory() {
+ return () -> new GlobalActions(mContext, mWindowManagerFuncs);
+ }
+ }
+
/** {@inheritDoc} */
@Override
- public void init(Context context, WindowManagerFuncs windowManagerFuncs) {
- mContext = context;
- mWindowManagerFuncs = windowManagerFuncs;
+ public void init(Context context, WindowManagerFuncs funcs) {
+ init(new Injector(context, funcs));
+ }
+
+ @VisibleForTesting
+ void init(Injector injector) {
+ mContext = injector.getContext();
+ mWindowManagerFuncs = injector.getWindowManagerFuncs();
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
@@ -1887,8 +1924,9 @@
mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE);
mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);
- mAccessibilityShortcutController =
- new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
+ mAccessibilityShortcutController = injector.getAccessibilityShortcutController(
+ mContext, new Handler(), mCurrentUserId);
+ mGlobalActionsFactory = injector.getGlobalActionsFactory();
mLogger = new MetricsLogger();
mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
@@ -1903,7 +1941,7 @@
res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress);
// Init display burn-in protection
- boolean burnInProtectionEnabled = context.getResources().getBoolean(
+ boolean burnInProtectionEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableBurnInProtection);
// Allow a system property to override this. Used by developer settings.
boolean burnInProtectionDevMode =
@@ -1921,7 +1959,7 @@
maxVertical = -4;
maxRadius = (isRoundWindow()) ? 6 : -1;
} else {
- Resources resources = context.getResources();
+ Resources resources = mContext.getResources();
minHorizontal = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMinHorizontalOffset);
maxHorizontal = resources.getInteger(
@@ -1934,21 +1972,21 @@
com.android.internal.R.integer.config_burnInProtectionMaxRadius);
}
mBurnInProtectionHelper = new BurnInProtectionHelper(
- context, minHorizontal, maxHorizontal, minVertical, maxVertical, maxRadius);
+ mContext, minHorizontal, maxHorizontal, minVertical, maxVertical, maxRadius);
}
mHandler = new PolicyHandler();
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
- mModifierShortcutManager = new ModifierShortcutManager(context);
- mUiMode = context.getResources().getInteger(
+ mModifierShortcutManager = new ModifierShortcutManager(mContext);
+ mUiMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mHomeIntent.addCategory(Intent.CATEGORY_HOME);
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- mEnableCarDockHomeCapture = context.getResources().getBoolean(
+ mEnableCarDockHomeCapture = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableCarDockHomeLaunch);
mCarDockIntent = new Intent(Intent.ACTION_MAIN, null);
mCarDockIntent.addCategory(Intent.CATEGORY_CAR_DOCK);
@@ -1963,7 +2001,7 @@
mVrHeadsetHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"PhoneWindowManager.mBroadcastWakeLock");
mPowerKeyWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -2035,9 +2073,9 @@
readConfigurationDependentBehaviors();
- mDisplayFoldController = DisplayFoldController.create(context, DEFAULT_DISPLAY);
+ mDisplayFoldController = DisplayFoldController.create(mContext, DEFAULT_DISPLAY);
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
// register for dock events
@@ -2047,7 +2085,7 @@
filter.addAction(UiModeManager.ACTION_ENTER_DESK_MODE);
filter.addAction(UiModeManager.ACTION_EXIT_DESK_MODE);
filter.addAction(Intent.ACTION_DOCK_EVENT);
- Intent intent = context.registerReceiver(mDockReceiver, filter);
+ Intent intent = mContext.registerReceiver(mDockReceiver, filter);
if (intent != null) {
// Retrieve current sticky dock event broadcast.
mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
@@ -2058,13 +2096,13 @@
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
- context.registerReceiver(mDreamReceiver, filter);
+ mContext.registerReceiver(mDreamReceiver, filter);
// register for multiuser-relevant broadcasts
filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- context.registerReceiver(mMultiuserReceiver, filter);
+ mContext.registerReceiver(mMultiuserReceiver, filter);
- mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
+ mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_safeModeEnabledVibePattern);
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
index 5964fa4..ebeb1455 100644
--- a/services/core/java/com/android/server/power/LowPowerStandbyController.java
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -201,6 +201,7 @@
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE),
false, mSettingsObserver, UserHandle.USER_ALL);
+ initSettingsLocked();
updateSettingsLocked();
if (mIsEnabled) {
@@ -212,6 +213,23 @@
}
@GuardedBy("mLock")
+ private void initSettingsLocked() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ if (mSupportedConfig) {
+ final int enabledSetting = Settings.Global.getInt(resolver,
+ Settings.Global.LOW_POWER_STANDBY_ENABLED, /* def= */ -1);
+
+ // If the ENABLED setting hasn't been assigned yet, set it to its default value.
+ // This ensures reading the setting reflects the enabled state, without having to know
+ // the default value for this device.
+ if (enabledSetting == -1) {
+ Settings.Global.putInt(resolver, Settings.Global.LOW_POWER_STANDBY_ENABLED,
+ /* value= */ mEnabledByDefaultConfig ? 1 : 0);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
private void updateSettingsLocked() {
final ContentResolver resolver = mContext.getContentResolver();
mIsEnabled = mSupportedConfig && Settings.Global.getInt(resolver,
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 09035cd..15c9ba9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -37,6 +37,7 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.util.Preconditions;
@@ -829,17 +830,41 @@
@Override
public void binderDied() {
// This is called whenever our client process dies.
+ SparseArray<ModelState.Activity> cachedMap =
+ new SparseArray<ModelState.Activity>();
synchronized (SoundTriggerMiddlewareValidation.this) {
- try {
- // Gracefully stop all active recognitions and unload the models.
+ // Copy the relevant state under the lock, so we can call back without
+ // holding a lock. This exposes us to a potential race, but the client is
+ // dead so we don't expect one.
+ // TODO(240613068) A more resilient fix for this.
for (Map.Entry<Integer, ModelState> entry :
mLoadedModels.entrySet()) {
- if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(entry.getKey());
- }
- mDelegate.unloadModel(entry.getKey());
+ cachedMap.put(entry.getKey(), entry.getValue().activityState);
}
- // Detach.
+ }
+ try {
+ // Gracefully stop all active recognitions and unload the models.
+ for (int i = 0; i < cachedMap.size(); i++) {
+ if (cachedMap.valueAt(i) == ModelState.Activity.ACTIVE) {
+ mDelegate.stopRecognition(cachedMap.keyAt(i));
+ }
+ mDelegate.unloadModel(cachedMap.keyAt(i));
+ }
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // Check if state updated unexpectedly to log race conditions.
+ for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
+ if (cachedMap.get(entry.getKey()) != entry.getValue().activityState) {
+ Log.e(TAG, "Unexpected state update in binderDied. Race occurred!");
+ }
+ }
+ if (mLoadedModels.size() != cachedMap.size()) {
+ Log.e(TAG, "Unexpected state update in binderDied. Race occurred!");
+ }
+ try {
+ // Detach
detachInternal();
} catch (Exception e) {
throw handleException(e);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index d7bfd1d..fcf0a90 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -263,8 +263,10 @@
}
});
- vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
- vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
+ if (vdm != null){
+ vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
+ vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
+ }
registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index c04b195..804689a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -422,7 +422,7 @@
if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
mAccessibilityTracing.logTrace(TAG + ".onSomeWindowResizedOrMoved",
FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
- "displayIds={" + displayIds.toString() + "}", "".getBytes(), callingUid);
+ "displayIds={" + Arrays.toString(displayIds) + "}", "".getBytes(), callingUid);
}
// Not relevant for the display magnifier.
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f8f94f6..63dcbf8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -79,6 +79,7 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY;
@@ -1582,13 +1583,6 @@
if (newParent != null && isState(RESUMED)) {
newParent.setResumedActivity(this, "onParentChanged");
- if (mStartingWindow != null && mStartingData != null
- && mStartingData.mAssociatedTask == null && newParent.isEmbedded()) {
- // The starting window should keep covering its task when the activity is
- // reparented to a task fragment that may not fill the task bounds.
- associateStartingDataWithTask();
- attachStartingSurfaceToAssociatedTask();
- }
mImeInsetsFrozenUntilStartInput = false;
}
@@ -2679,14 +2673,17 @@
}
}
+ /** Called when the starting window is added to this activity. */
void attachStartingWindow(@NonNull WindowState startingWindow) {
startingWindow.mStartingData = mStartingData;
mStartingWindow = startingWindow;
+ // The snapshot type may have called associateStartingDataWithTask().
if (mStartingData != null && mStartingData.mAssociatedTask != null) {
attachStartingSurfaceToAssociatedTask();
}
}
+ /** Makes starting window always fill the associated task. */
private void attachStartingSurfaceToAssociatedTask() {
// Associate the configuration of starting window with the task.
overrideConfigurationPropagation(mStartingWindow, mStartingData.mAssociatedTask);
@@ -2694,6 +2691,7 @@
mStartingData.mAssociatedTask.mSurfaceControl);
}
+ /** Called when the starting window is not added yet but its data is known to fill the task. */
private void associateStartingDataWithTask() {
mStartingData.mAssociatedTask = task;
task.forAllActivities(r -> {
@@ -2703,6 +2701,16 @@
});
}
+ /** Associates and attaches an added starting window to the current task. */
+ void associateStartingWindowWithTaskIfNeeded() {
+ if (mStartingWindow == null || mStartingData == null
+ || mStartingData.mAssociatedTask != null) {
+ return;
+ }
+ associateStartingDataWithTask();
+ attachStartingSurfaceToAssociatedTask();
+ }
+
void removeStartingWindow() {
boolean prevEligibleForLetterboxEducation = isEligibleForLetterboxEducation();
@@ -8802,9 +8810,25 @@
* Returns the min aspect ratio of this activity.
*/
float getMinAspectRatio() {
- if (info.applicationInfo == null || !info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || (
- info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
- && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation()))) {
+ if (info.applicationInfo == null) {
+ return info.getMinAspectRatio();
+ }
+
+ if (!info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO)) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+ && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation())) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN)
+ && getParent().getConfiguration().orientation == ORIENTATION_PORTRAIT
+ && getParent().getWindowConfiguration().getWindowingMode()
+ == WINDOWING_MODE_FULLSCREEN) {
+ // We are using the parent configuration here as this is the most recent one that gets
+ // passed to onConfigurationChanged when a relevant change takes place
return info.getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 619d693..9ad62af 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1866,6 +1866,16 @@
final ActivityRecord targetTaskTop = newTask
? null : targetTask.getTopNonFinishingActivity();
if (targetTaskTop != null) {
+ // Removes the existing singleInstance activity in another task (if any) while
+ // launching a singleInstance activity on sourceRecord's task.
+ if (LAUNCH_SINGLE_INSTANCE == mLaunchMode && mSourceRecord != null
+ && targetTask == mSourceRecord.getTask()) {
+ final ActivityRecord activity = mRootWindowContainer.findActivity(mIntent,
+ mStartActivity.info, false);
+ if (activity != null && activity.getTask() != targetTask) {
+ activity.destroyIfPossible("Removes redundant singleInstance");
+ }
+ }
// Recycle the target task for this launch.
startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);
if (startResult != START_SUCCESS) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1c90bba..6ee0186 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3188,8 +3188,12 @@
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
mPointerEventDispatcher.dispose();
setRotationAnimation(null);
+ // Unlink death from remote to clear the reference from binder -> mRemoteInsetsDeath
+ // -> this DisplayContent.
+ setRemoteInsetsController(null);
mWmService.mAnimator.removeDisplayLocked(mDisplayId);
mOverlayLayer.release();
+ mWindowingLayer.release();
mInputMonitor.onDisplayRemoved();
mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 508e6dc..d4eac8d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -362,6 +362,7 @@
private WindowState mTopFullscreenOpaqueWindowState;
private boolean mTopIsFullscreen;
private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
+ private boolean mForceConsumeSystemBars;
private boolean mForceShowSystemBars;
private boolean mShowingDream;
@@ -1561,6 +1562,13 @@
}
/**
+ * @return true if the system bars are forced to be consumed
+ */
+ public boolean areSystemBarsForcedConsumedLw() {
+ return mForceConsumeSystemBars;
+ }
+
+ /**
* @return true if the system bars are forced to stay visible
*/
public boolean areSystemBarsForcedShownLw() {
@@ -2462,6 +2470,10 @@
// We need to force showing system bars when the multi-window or freeform root task is
// visible.
mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible;
+ // We need to force the consumption of the system bars if they are force shown or if they
+ // are controlled by a remote insets controller.
+ mForceConsumeSystemBars = mForceShowSystemBars
+ || mDisplayContent.getInsetsPolicy().remoteInsetsControllerControlsSystemBars(win);
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
final boolean topAppHidesStatusBar = topAppHidesStatusBar();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e38f5fe..659e37b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -60,10 +60,7 @@
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -1436,6 +1433,13 @@
final TaskFragment childTaskFrag = child.asTaskFragment();
if (childTaskFrag != null && childTaskFrag.asTask() == null) {
childTaskFrag.setMinDimensions(mMinWidth, mMinHeight);
+
+ // The starting window should keep covering its task when a pure TaskFragment is added
+ // because its bounds may not fill the task.
+ final ActivityRecord top = getTopMostActivity();
+ if (top != null) {
+ top.associateStartingWindowWithTaskIfNeeded();
+ }
}
}
@@ -4966,23 +4970,17 @@
dc.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(r);
} else {
- int transit = TRANSIT_OLD_ACTIVITY_OPEN;
- if (newTask) {
- if (r.mLaunchTaskBehind) {
- transit = TRANSIT_OLD_TASK_OPEN_BEHIND;
- } else {
- // If a new task is being launched, then mark the existing top activity as
- // supporting picture-in-picture while pausing only if the starting activity
- // would not be considered an overlay on top of the current activity
- // (eg. not fullscreen, or the assistant)
- enableEnterPipOnTaskSwitch(pipCandidate,
- null /* toFrontTask */, r, options);
- transit = TRANSIT_OLD_TASK_OPEN;
- }
- }
dc.prepareAppTransition(TRANSIT_OPEN);
mTaskSupervisor.mNoAnimActivities.remove(r);
}
+ if (newTask && !r.mLaunchTaskBehind) {
+ // If a new task is being launched, then mark the existing top activity as
+ // supporting picture-in-picture while pausing only if the starting activity
+ // would not be considered an overlay on top of the current activity
+ // (eg. not fullscreen, or the assistant)
+ enableEnterPipOnTaskSwitch(pipCandidate,
+ null /* toFrontTask */, r, options);
+ }
boolean doShow = true;
if (newTask) {
// Even though this activity is starting fresh, we still need
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index b8d2feb..09fd900 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -53,6 +53,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -424,7 +425,7 @@
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
- " files=" + files);
+ " files=" + Arrays.toString(files));
if (files == null) {
Slog.e(TAG, "File error accessing recents directory (directory doesn't exist?).");
return;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8025cb2..8aa58b2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1808,7 +1808,7 @@
prepareNoneTransitionForRelaunching(activity);
}
- if (displayPolicy.areSystemBarsForcedShownLw()) {
+ if (displayPolicy.areSystemBarsForcedConsumedLw()) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
@@ -2527,7 +2527,7 @@
if (win.mActivityRecord != null) {
win.mActivityRecord.updateReportedVisibilityLocked();
}
- if (displayPolicy.areSystemBarsForcedShownLw()) {
+ if (displayPolicy.areSystemBarsForcedConsumedLw()) {
result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
}
if (!win.isGoneForLayout()) {
@@ -8896,7 +8896,7 @@
: overrideScale;
outInsetsState.scale(1f / compatScale);
}
- return dc.getDisplayPolicy().areSystemBarsForcedShownLw();
+ return dc.getDisplayPolicy().areSystemBarsForcedConsumedLw();
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3469303..2e7e78d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3917,7 +3917,7 @@
final boolean forceRelayout = syncWithBuffers || isDragResizeChanged;
final DisplayContent displayContent = getDisplayContent();
final boolean alwaysConsumeSystemBars =
- displayContent.getDisplayPolicy().areSystemBarsForcedShownLw();
+ displayContent.getDisplayPolicy().areSystemBarsForcedConsumedLw();
final int displayId = displayContent.getDisplayId();
if (isDragResizeChanged) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4078996..5d6ffd8 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -37,6 +37,7 @@
"com_android_server_ConsumerIrService.cpp",
"com_android_server_companion_virtual_InputController.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
+ "com_android_server_display_DisplayControl.cpp",
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_gpu_GpuService.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
@@ -89,6 +90,7 @@
cc_defaults {
name: "libservices.core-libs",
+ defaults: ["android.hardware.graphics.common-ndk_shared"],
shared_libs: [
"libadb_pairing_server",
"libadb_pairing_connection",
@@ -158,7 +160,6 @@
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"android.hardware.input.processor-V1-ndk",
"android.hardware.ir@1.0",
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
new file mode 100644
index 0000000..61f2b14
--- /dev/null
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <android_util_Binder.h>
+#include <gui/SurfaceComposerClient.h>
+#include <jni.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+namespace android {
+
+static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj, jboolean secure) {
+ ScopedUtfChars name(env, nameObj);
+ sp<IBinder> token(SurfaceComposerClient::createDisplay(String8(name.c_str()), bool(secure)));
+ return javaObjectForIBinder(env, token);
+}
+
+static void nativeDestroyDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return;
+ SurfaceComposerClient::destroyDisplay(token);
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod sDisplayMethods[] = {
+ // clang-format off
+ {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
+ (void*)nativeCreateDisplay },
+ {"nativeDestroyDisplay", "(Landroid/os/IBinder;)V",
+ (void*)nativeDestroyDisplay },
+ // clang-format on
+};
+
+int register_com_android_server_display_DisplayControl(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/display/DisplayControl",
+ sDisplayMethods, NELEM(sDisplayMethods));
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 0cd9494..00bef09 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -64,6 +64,7 @@
int register_android_server_companion_virtual_InputController(JNIEnv* env);
int register_android_server_app_GameManagerService(JNIEnv* env);
int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
+int register_com_android_server_display_DisplayControl(JNIEnv* env);
};
using namespace android;
@@ -120,5 +121,6 @@
register_android_server_companion_virtual_InputController(env);
register_android_server_app_GameManagerService(env);
register_com_android_server_wm_TaskFpsCallbackController(env);
+ register_com_android_server_display_DisplayControl(env);
return JNI_VERSION_1_4;
}
diff --git a/services/core/xsd/display-device-config/autobrightness.xsd b/services/core/xsd/display-device-config/autobrightness.xsd
new file mode 100644
index 0000000..477625a
--- /dev/null
+++ b/services/core/xsd/display-device-config/autobrightness.xsd
@@ -0,0 +1,33 @@
+<!--
+ 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.
+-->
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:complexType name="autoBrightness">
+ <xs:sequence>
+ <!-- Sets the debounce for autoBrightness brightening in millis-->
+ <xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Sets the debounce for autoBrightness darkening in millis-->
+ <xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+</xs:schema>
\ No newline at end of file
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 98f83d8..bea5e2c 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -23,6 +23,7 @@
<xs:schema version="2.0"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:include schemaLocation="autobrightness.xsd" />
<xs:element name="displayConfiguration">
<xs:complexType>
<xs:sequence>
@@ -342,74 +343,4 @@
<xs:annotation name="final"/>
</xs:element>
</xs:complexType>
-
- <xs:complexType name="autoBrightness">
- <xs:sequence>
- <!-- Sets the debounce for autoBrightness brightening in millis-->
- <xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
- minOccurs="0" maxOccurs="1">
- <xs:annotation name="final"/>
- </xs:element>
- <!-- Sets the debounce for autoBrightness darkening in millis-->
- <xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
- minOccurs="0" maxOccurs="1">
- <xs:annotation name="final"/>
- </xs:element>
- <!-- Sets the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
- minOccurs="0" maxOccurs="1">
- <xs:annotation name="final"/>
- </xs:element>
- </xs:sequence>
- </xs:complexType>
-
- <!-- Represents the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:complexType name="displayBrightnessMapping">
- <xs:sequence>
- <!-- Sets the list of display brightness points, each representing the desired screen
- brightness in nits to the corresponding lux for the current display
-
- The N entries of this array define N + 1 control points as follows:
- (1-based arrays)
-
- Point 1: (0, nits[1]): currentLux <= 0
- Point 2: (lux[1], nits[2]): 0 < currentLux <= lux[1]
- Point 3: (lux[2], nits[3]): lux[2] < currentLux <= lux[3]
- ...
- Point N+1: (lux[N], nits[N+1]): lux[N] < currentLux
-
- The control points must be strictly increasing. Each control point
- corresponds to an entry in the brightness backlight values arrays.
- For example, if currentLux == lux[1] (first element of the levels array)
- then the brightness will be determined by nits[2] (second element
- of the brightness values array).
- -->
- <xs:element name="displayBrightnessPoint" type="displayBrightnessPoint"
- minOccurs="1" maxOccurs="unbounded">
- <xs:annotation name="final"/>
- </xs:element>
- </xs:sequence>
- </xs:complexType>
-
- <!-- Represents a point in the display brightness mapping, representing the lux level from the
- light sensor to the desired screen brightness in nits at this level -->
- <xs:complexType name="displayBrightnessPoint">
- <xs:sequence>
- <!-- The lux level from the light sensor. This must be a non-negative integer -->
- <xs:element name="lux" type="xs:nonNegativeInteger"
- minOccurs="1" maxOccurs="1">
- <xs:annotation name="final"/>
- </xs:element>
-
- <!-- Desired screen brightness in nits corresponding to the suggested lux values.
- The display brightness is defined as the measured brightness of an all-white image.
- This must be a non-negative integer -->
- <xs:element name="nits" type="xs:nonNegativeInteger"
- minOccurs="1" maxOccurs="1">
- <xs:annotation name="final"/>
- </xs:element>
- </xs:sequence>
- </xs:complexType>
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index e5d2617..e9a9269 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -5,10 +5,8 @@
ctor public AutoBrightness();
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
- method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
- method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
}
public class BrightnessThresholds {
@@ -45,19 +43,6 @@
method public java.util.List<com.android.server.display.config.Density> getDensity();
}
- public class DisplayBrightnessMapping {
- ctor public DisplayBrightnessMapping();
- method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint();
- }
-
- public class DisplayBrightnessPoint {
- ctor public DisplayBrightnessPoint();
- method public final java.math.BigInteger getLux();
- method public final java.math.BigInteger getNits();
- method public final void setLux(java.math.BigInteger);
- method public final void setNits(java.math.BigInteger);
- }
-
public class DisplayConfiguration {
ctor public DisplayConfiguration();
method @NonNull public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholds();
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index f454ac7..f1dc1fa 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -10,6 +10,8 @@
cc_library_shared {
name: "libmockingservicestestjni",
+ defaults: ["android.hardware.graphics.common-ndk_shared"],
+
cflags: [
"-Wall",
"-Werror",
@@ -48,7 +50,6 @@
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"android.hidl.token@1.0-utils",
],
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
new file mode 100644
index 0000000..130b02d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService.Injector;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Common tests for {@link BroadcastQueue} implementations.
+ */
+@MediumTest
+@RunWith(Parameterized.class)
+public class BroadcastQueueTest {
+ private static final String TAG = "BroadcastQueueTest";
+
+ @Rule
+ public final ApplicationExitInfoTest.ServiceThreadRule
+ mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+
+ private final Impl mImpl;
+
+ private enum Impl {
+ DEFAULT
+ }
+
+ private Context mContext;
+ private HandlerThread mHandlerThread;
+
+ @Mock
+ private AppOpsService mAppOpsService;
+ @Mock
+ private PackageManagerInternal mPackageManagerInt;
+
+ private ActivityManagerService mAms;
+ private BroadcastQueue mQueue;
+
+ @Parameters(name = "impl={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {Impl.DEFAULT} });
+ }
+
+ public BroadcastQueueTest(Impl impl) {
+ mImpl = impl;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+ doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
+
+ final ActivityManagerService realAms = new ActivityManagerService(
+ new TestInjector(mContext), mServiceThreadRule.getThread());
+ realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+ realAms.mPackageManagerInt = mPackageManagerInt;
+ mAms = spy(realAms);
+
+ final BroadcastConstants constants = new BroadcastConstants(
+ Settings.Global.BROADCAST_FG_CONSTANTS);
+ final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
+ public boolean shouldSkip(BroadcastRecord r, ResolveInfo info) {
+ return false;
+ }
+ public boolean shouldSkip(BroadcastRecord r, BroadcastFilter filter) {
+ return false;
+ }
+ };
+
+ if (mImpl == Impl.DEFAULT) {
+ mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
+ constants, emptySkipPolicy, false);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private class TestInjector extends Injector {
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+ }
+
+ private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
+ final ProcessRecord r = new ProcessRecord(mAms, makeApplicationInfo(packageName), null,
+ getUidForPackage(packageName));
+ final IApplicationThread 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));
+
+ doAnswer((invocation) -> {
+ Log.v(TAG, "Delivering finishReceiverLocked() for "
+ + Arrays.toString(invocation.getArguments()));
+ mQueue.finishReceiverLocked(threadBinder, Activity.RESULT_OK,
+ null, null, false, false);
+ return null;
+ }).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(),
+ anyInt(), anyInt());
+
+ return r;
+ }
+
+ private ApplicationInfo makeApplicationInfo(String packageName) {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.uid = getUidForPackage(packageName);
+ return ai;
+ }
+
+ private ResolveInfo makeManifestReceiver(String packageName, String name) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = packageName;
+ ri.activityInfo.name = name;
+ ri.activityInfo.applicationInfo = makeApplicationInfo(packageName);
+ return ri;
+ }
+
+ private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
+ List receivers) {
+ return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), receivers);
+ }
+
+ private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
+ BroadcastOptions options, List receivers) {
+ return new BroadcastRecord(mQueue, intent, callerApp, callerApp.info.packageName, null,
+ callerApp.getPid(), callerApp.info.uid, false, null, null, null, null,
+ AppOpsManager.OP_NONE, options, receivers, null, Activity.RESULT_OK, null, null,
+ false, false, false, UserHandle.USER_SYSTEM, false, null, false, null);
+ }
+
+ private ArgumentMatcher<Intent> filterEqualsIgnoringComponent(Intent intent) {
+ final Intent intentClean = new Intent(intent);
+ intentClean.setComponent(null);
+ return (test) -> {
+ final Intent testClean = new Intent(test);
+ testClean.setComponent(null);
+ return intentClean.filterEquals(testClean);
+ };
+ }
+
+ private void waitForIdle() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ if (mQueue.isIdle()) break;
+ SystemClock.sleep(100);
+ }
+ assertTrue(mQueue.isIdle());
+ }
+
+ private static final String PACKAGE_RED = "com.example.red";
+ private static final String PACKAGE_GREEN = "com.example.green";
+ private static final String PACKAGE_BLUE = "com.example.blue";
+
+ private static final String CLASS_RED = "com.example.red.Red";
+ private static final String CLASS_GREEN = "com.example.green.Green";
+ private static final String CLASS_BLUE = "com.example.blue.Blue";
+
+ private static int getUidForPackage(String packageName) {
+ switch (packageName) {
+ case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
+ case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
+ case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
+ default: throw new IllegalArgumentException();
+ }
+ }
+
+ @Test
+ public void testSimple_Manifest_Warm() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+
+ waitForIdle();
+ verify(receiverApp.getThread()).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ }
+
+ @Test
+ public void testSimple_Manifest_Warm_Multiple() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+
+ waitForIdle();
+ verify(receiverGreenApp.getThread()).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(timezone)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ verify(receiverBlueApp.getThread()).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(timezone)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ verify(receiverBlueApp.getThread()).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(airplane)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ }
+
+ // TODO: verify registered receiver in warm app
+ // TODO: verify manifest receiver in cold app
+
+ // TODO: verify mixing multiple manifest and registered receivers of same broadcast
+ // TODO: verify delivery of 3 distinct broadcasts
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
index 03eff2a..e170e98 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
@@ -258,7 +258,7 @@
PROVIDER_A_SERVICE_A,
PROVIDER_A_SERVICE_C));
FakeGameServiceProviderInstance instanceB =
- seedConfigurationForUser(USER_10, configurationA);
+ seedConfigurationForUser(USER_10, configurationB);
Intent intent = new Intent();
intent.setData(Uri.parse("package:" + PROVIDER_A_PACKAGE_NAME));
broadcastReceiverArgumentCaptor.getValue().onReceive(mMockContext, intent);
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index e4f9eaf..3f16a98 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -28,7 +28,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,7 +35,6 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
-import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManagerInternal;
@@ -98,7 +96,6 @@
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Objects;
import java.util.function.Consumer;
@@ -361,7 +358,6 @@
throws Exception {
mGameServiceProviderInstance.start();
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
@@ -383,7 +379,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation =
@@ -398,7 +393,6 @@
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
@@ -408,7 +402,6 @@
public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -426,9 +419,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
CreateGameSessionRequest expectedCreateGameSessionRequest = new CreateGameSessionRequest(10,
@@ -442,7 +433,6 @@
public void gameSessionSuccessfullyCreated_createsTaskOverlay() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -462,7 +452,6 @@
startTask(10, GAME_A_MAIN_ACTIVITY);
startProcessForPackage(gameProcessId, GAME_A_PACKAGE);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -488,7 +477,6 @@
startTask(11, GAME_A_MAIN_ACTIVITY);
startProcessForPackage(gameProcessId, GAME_A_PACKAGE);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
mFakeGameService.requestCreateGameSession(11);
@@ -522,7 +510,6 @@
startProcessForPackage(firstGameProcessId, GAME_A_PACKAGE);
startProcessForPackage(secondGameProcessId, GAME_A_PACKAGE);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -551,7 +538,6 @@
startTask(10, GAME_A_MAIN_ACTIVITY);
startProcessForPackage(firstGameProcessId, GAME_A_PACKAGE);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -583,7 +569,6 @@
startTask(11, GAME_A_MAIN_ACTIVITY);
startProcessForPackage(firstGameProcessId, GAME_A_PACKAGE);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
mFakeGameService.requestCreateGameSession(11);
@@ -624,7 +609,6 @@
startTask(10, GAME_A_MAIN_ACTIVITY);
startProcessForPackage(gameProcessId, GAME_A_PACKAGE);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
// No game session should be created.
assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
@@ -636,7 +620,6 @@
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -677,7 +660,6 @@
public void systemBarsTransientShownDueToGesture_hasGameSession_propagatesToGameSession() {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -699,7 +681,6 @@
public void systemBarsTransientShownButNotGesture_hasGameSession_notPropagatedToGameSession() {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -721,7 +702,6 @@
public void gameTaskFocused_propagatedToGameSession() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -747,7 +727,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -764,7 +743,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
dispatchTaskRemoved(10);
@@ -782,7 +760,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -800,7 +777,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -830,7 +806,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -851,7 +826,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -879,7 +853,6 @@
startTask(10, GAME_A_MAIN_ACTIVITY);
startTask(11, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -897,7 +870,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -925,7 +897,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -955,7 +926,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -989,19 +959,10 @@
}
@Test
- public void createGameSession_failurePermissionDenied() throws Exception {
- mGameServiceProviderInstance.start();
- startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY);
- assertThrows(SecurityException.class, () -> mFakeGameService.requestCreateGameSession(10));
- }
-
- @Test
public void gameSessionServiceDies_severalActiveGameSessions_destroysGameSessions() {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -1030,7 +991,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -1058,7 +1018,6 @@
public void takeScreenshot_failureNoBitmapCaptured() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -1093,7 +1052,6 @@
any(), any(), any(), anyInt(), anyInt(), any(), anyInt(), any(), any());
mGameServiceProviderInstance.start();
startTask(taskId, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(taskId);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -1113,7 +1071,6 @@
@Test
public void restartGame_taskIdAssociatedWithGame_restartsTargetGame() throws Exception {
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
Intent launchIntent = new Intent("com.test.ACTION_LAUNCH_GAME_PACKAGE")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
when(mMockPackageManager.getLaunchIntentForPackage(GAME_A_PACKAGE))
@@ -1122,7 +1079,6 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -1148,11 +1104,9 @@
@Test
public void restartGame_taskIdNotAssociatedWithGame_noOp() throws Exception {
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -1170,21 +1124,6 @@
.restartTaskActivityProcessIfVisible(anyInt(), anyString());
}
- @Test
- public void restartGame_failurePermissionDenied() throws Exception {
- mGameServiceProviderInstance.start();
- startTask(10, GAME_A_MAIN_ACTIVITY);
- mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
- mFakeGameService.requestCreateGameSession(10);
- IGameSessionController gameSessionController = Objects.requireNonNull(getOnlyElement(
- mFakeGameSessionService.getCapturedCreateInvocations())).mGameSessionController;
- mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY);
- assertThrows(SecurityException.class,
- () -> gameSessionController.restartGame(10));
- verify(mActivityTaskManagerInternal, never())
- .restartTaskActivityProcessIfVisible(anyInt(), anyString());
- }
-
private void startTask(int taskId, ComponentName componentName) {
addRunningTaskInfo(taskId, componentName);
@@ -1261,14 +1200,6 @@
}
}
- private void mockPermissionGranted(String permission) {
- mMockContext.setPermission(permission, PackageManager.PERMISSION_GRANTED);
- }
-
- private void mockPermissionDenied(String permission) {
- mMockContext.setPermission(permission, PackageManager.PERMISSION_DENIED);
- }
-
private void dispatchTaskSystemBarsEvent(
ThrowingConsumer<TaskSystemBarsListener> taskSystemBarsListenerConsumer) {
for (TaskSystemBarsListener listener : mTaskSystemBarsListeners) {
@@ -1409,42 +1340,12 @@
}
private final class MockContext extends ContextWrapper {
- // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
- private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
-
MockContext(Context base) {
super(base);
}
-
- /**
- * Mock checks for the specified permission, and have them behave as per {@code granted}.
- *
- * <p>Passing null reverts to default behavior, which does a real permission check on the
- * test package.
- *
- * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
- * {@link PackageManager#PERMISSION_DENIED}.
- */
- public void setPermission(String permission, Integer granted) {
- mMockedPermissions.put(permission, granted);
- }
-
@Override
public PackageManager getPackageManager() {
return mMockPackageManager;
}
-
- @Override
- public void enforceCallingPermission(String permission, @Nullable String message) {
- final Integer granted = mMockedPermissions.get(permission);
- if (granted == null) {
- super.enforceCallingOrSelfPermission(permission, message);
- return;
- }
-
- if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
- throw new SecurityException("[Test] permission denied: " + permission);
- }
- }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 60ddeeb..7755552 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -149,12 +149,6 @@
.thenReturn(mockArray);
when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerBottomRadiusArray))
.thenReturn(mockArray);
- when(mMockedResources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
- .thenReturn(mockArray);
- when(mMockedResources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessLevels))
- .thenReturn(mockArray);
}
@After
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 9d26971..fb9cbb0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -61,7 +61,6 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -1029,44 +1028,4 @@
return mPackageInfo.applicationInfo.splitSourceDirs[length - 1];
}
}
-
- private boolean shouldPackageRunOob(boolean isDefaultEnabled, String whitelist,
- Collection<String> packageNamesInSameProcess) {
- return DexManager.isPackageSelectedToRunOobInternal(
- isDefaultEnabled, whitelist, packageNamesInSameProcess);
- }
-
- @Test
- public void testOobPackageSelectionDefault() {
- // Feature is off by default, not overriden
- assertFalse(shouldPackageRunOob(false, "ALL", null));
- }
-
- @Test
- public void testOobPackageSelectionWhitelist() {
- // Various allowlist of apps to run in OOB mode.
- final String kWhitelistApp0 = "com.priv.app0";
- final String kWhitelistApp1 = "com.priv.app1";
- final String kWhitelistApp2 = "com.priv.app2";
- final String kWhitelistApp1AndApp2 = "com.priv.app1,com.priv.app2";
-
- // Packages that shares the targeting process.
- final Collection<String> runningPackages = Arrays.asList("com.priv.app1", "com.priv.app2");
-
- // Feature is off, allowlist does not matter
- assertFalse(shouldPackageRunOob(false, kWhitelistApp0, runningPackages));
- assertFalse(shouldPackageRunOob(false, kWhitelistApp1, runningPackages));
- assertFalse(shouldPackageRunOob(false, "", runningPackages));
- assertFalse(shouldPackageRunOob(false, "ALL", runningPackages));
-
- // Feature is on, app not in allowlist
- assertFalse(shouldPackageRunOob(true, kWhitelistApp0, runningPackages));
- assertFalse(shouldPackageRunOob(true, "", runningPackages));
-
- // Feature is on, app in allowlist
- assertTrue(shouldPackageRunOob(true, kWhitelistApp1, runningPackages));
- assertTrue(shouldPackageRunOob(true, kWhitelistApp2, runningPackages));
- assertTrue(shouldPackageRunOob(true, kWhitelistApp1AndApp2, runningPackages));
- assertTrue(shouldPackageRunOob(true, "ALL", runningPackages));
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 59b69f9..233caf9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -837,7 +837,7 @@
@Override
public void describeTo(Description description) {
- description.appendText("Contains points " + points);
+ description.appendText("Contains points " + Arrays.toString(points));
}
};
}
diff --git a/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java
index 6bb494d..c75abf8 100644
--- a/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java
@@ -21,8 +21,8 @@
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.IAmbientContextObserver;
import android.content.Intent;
-import android.os.RemoteCallback;
import android.os.UserHandle;
import androidx.test.InstrumentationRegistry;
@@ -30,6 +30,8 @@
import org.junit.Test;
+import java.util.List;
+
/**
* Unit test for {@link AmbientContextManagerService}.
* atest FrameworksServicesTests:AmbientContextManagerServiceTest
@@ -48,12 +50,22 @@
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstrumentationRegistry.getTargetContext(), 0,
intent, PendingIntent.FLAG_IMMUTABLE);
+ IAmbientContextObserver observer = new IAmbientContextObserver.Stub() {
+ @Override
+ public void onEvents(List<AmbientContextEvent> events) {
+ }
+
+ @Override
+ public void onRegistrationComplete(int statusCode) {
+ }
+ };
AmbientContextManagerService.ClientRequest clientRequest =
new AmbientContextManagerService.ClientRequest(USER_ID, request,
- pendingIntent, new RemoteCallback(result -> {}));
+ pendingIntent.getCreatorPackage(), observer);
assertThat(clientRequest.getRequest()).isEqualTo(request);
assertThat(clientRequest.getPackageName()).isEqualTo(SYSTEM_PACKAGE_NAME);
+ assertThat(clientRequest.getObserver()).isEqualTo(observer);
assertThat(clientRequest.hasUserId(USER_ID)).isTrue();
assertThat(clientRequest.hasUserId(-1)).isFalse();
assertThat(clientRequest.hasUserIdAndPackageName(USER_ID, SYSTEM_PACKAGE_NAME)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java
new file mode 100644
index 0000000..10f0a5c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.server.biometrics.log;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class ALSProbeTest {
+
+ private static final long TIMEOUT_MS = 1000;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private SensorManager mSensorManager;
+ @Captor
+ private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+ private TestableLooper mLooper;
+ private Sensor mLightSensor = new Sensor(
+ new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
+ "", "", 0, 0, 0));
+
+ private ALSProbe mProbe;
+
+ @Before
+ public void setup() {
+ mLooper = TestableLooper.get(this);
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mLightSensor);
+ mProbe = new ALSProbe(mSensorManager, new Handler(mLooper.getLooper()), TIMEOUT_MS - 1);
+ reset(mSensorManager);
+ }
+
+ @Test
+ public void testEnable() {
+ final float value = 2.0f;
+ mProbe.enable();
+ verify(mSensorManager).registerListener(
+ mSensorEventListenerCaptor.capture(), any(), anyInt());
+
+ mSensorEventListenerCaptor.getValue().onSensorChanged(
+ new SensorEvent(mLightSensor, 1, 1, new float[]{4.0f}));
+ mSensorEventListenerCaptor.getValue().onSensorChanged(
+ new SensorEvent(mLightSensor, 1, 2, new float[]{value}));
+
+ assertThat(mProbe.getCurrentLux()).isEqualTo(value);
+ }
+
+ @Test
+ public void testEnableOnlyOnce() {
+ mProbe.enable();
+ mProbe.enable();
+
+ verify(mSensorManager).registerListener(any(), any(), anyInt());
+ verifyNoMoreInteractions(mSensorManager);
+ }
+
+ @Test
+ public void testDisable() {
+ mProbe.enable();
+ verify(mSensorManager).registerListener(
+ mSensorEventListenerCaptor.capture(), any(), anyInt());
+ mProbe.disable();
+
+ verify(mSensorManager).unregisterListener(eq(mSensorEventListenerCaptor.getValue()));
+ verifyNoMoreInteractions(mSensorManager);
+ }
+
+ @Test
+ public void testDestroy() {
+ mProbe.destroy();
+ mProbe.enable();
+
+ verify(mSensorManager, never()).registerListener(any(), any(), anyInt());
+ verifyNoMoreInteractions(mSensorManager);
+ }
+
+ @Test
+ public void testDisabledReportsNegativeValue() {
+ assertThat(mProbe.getCurrentLux()).isLessThan(0f);
+
+ mProbe.enable();
+ verify(mSensorManager).registerListener(
+ mSensorEventListenerCaptor.capture(), any(), anyInt());
+ mSensorEventListenerCaptor.getValue().onSensorChanged(
+ new SensorEvent(mLightSensor, 1, 1, new float[]{4.0f}));
+ mProbe.disable();
+
+ assertThat(mProbe.getCurrentLux()).isLessThan(0f);
+ }
+
+ @Test
+ public void testWatchDog() {
+ mProbe.enable();
+ verify(mSensorManager).registerListener(
+ mSensorEventListenerCaptor.capture(), any(), anyInt());
+ mSensorEventListenerCaptor.getValue().onSensorChanged(
+ new SensorEvent(mLightSensor, 1, 1, new float[]{4.0f}));
+ moveTimeBy(TIMEOUT_MS);
+
+ verify(mSensorManager).unregisterListener(eq(mSensorEventListenerCaptor.getValue()));
+ verifyNoMoreInteractions(mSensorManager);
+ assertThat(mProbe.getCurrentLux()).isLessThan(0f);
+ }
+
+ @Test
+ public void testEnableExtendsWatchDog() {
+ mProbe.enable();
+ verify(mSensorManager).registerListener(any(), any(), anyInt());
+
+ moveTimeBy(TIMEOUT_MS / 2);
+ verify(mSensorManager, never()).unregisterListener(any(SensorEventListener.class));
+
+ mProbe.enable();
+ moveTimeBy(TIMEOUT_MS);
+
+ verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+ verifyNoMoreInteractions(mSensorManager);
+ assertThat(mProbe.getCurrentLux()).isLessThan(0f);
+ }
+
+ private void moveTimeBy(long millis) {
+ mLooper.moveTimeForward(millis);
+ mLooper.processAllMessages();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index e6acc90..dd7aeb7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -40,8 +40,6 @@
import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBarService;
-import com.google.common.collect.ImmutableList;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -89,33 +87,64 @@
@Test
public void testIsAod() throws RemoteException {
- mListener.onDozeChanged(true);
+ mListener.onDozeChanged(true /* isDozing */, false /* isAwake */);
assertThat(mProvider.isAod()).isTrue();
- mListener.onDozeChanged(false);
+ mListener.onDozeChanged(false /* isDozing */, false /* isAwake */);
assertThat(mProvider.isAod()).isFalse();
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(false);
- mListener.onDozeChanged(true);
+ mListener.onDozeChanged(true /* isDozing */, false /* isAwake */);
assertThat(mProvider.isAod()).isFalse();
- mListener.onDozeChanged(false);
+ mListener.onDozeChanged(false /* isDozing */, false /* isAwake */);
assertThat(mProvider.isAod()).isFalse();
}
@Test
+ public void testIsAwake() throws RemoteException {
+ mListener.onDozeChanged(false /* isDozing */, true /* isAwake */);
+ assertThat(mProvider.isAwake()).isTrue();
+ mListener.onDozeChanged(false /* isDozing */, false /* isAwake */);
+ assertThat(mProvider.isAwake()).isFalse();
+ mListener.onDozeChanged(true /* isDozing */, true /* isAwake */);
+ assertThat(mProvider.isAwake()).isTrue();
+ mListener.onDozeChanged(true /* isDozing */, false /* isAwake */);
+ assertThat(mProvider.isAwake()).isFalse();
+ }
+
+ @Test
public void testSubscribesToAod() throws RemoteException {
- final List<Boolean> expected = ImmutableList.of(true, false, true, true, false);
final List<Boolean> actual = new ArrayList<>();
mProvider.subscribe(mOpContext, ctx -> {
assertThat(ctx).isSameInstanceAs(mOpContext);
+ assertThat(mProvider.isAod()).isEqualTo(ctx.isAod);
+ assertThat(mProvider.isAwake()).isFalse();
actual.add(ctx.isAod);
});
- for (boolean v : expected) {
- mListener.onDozeChanged(v);
+ for (boolean v : List.of(true, false, true, true, false, false)) {
+ mListener.onDozeChanged(v /* isDozing */, false /* isAwake */);
}
- assertThat(actual).containsExactlyElementsIn(expected).inOrder();
+ assertThat(actual).containsExactly(true, false, true, false).inOrder();
+ }
+
+ @Test
+ public void testSubscribesToAwake() throws RemoteException {
+ final List<Boolean> actual = new ArrayList<>();
+
+ mProvider.subscribe(mOpContext, ctx -> {
+ assertThat(ctx).isSameInstanceAs(mOpContext);
+ assertThat(ctx.isAod).isFalse();
+ assertThat(mProvider.isAod()).isFalse();
+ actual.add(mProvider.isAwake());
+ });
+
+ for (boolean v : List.of(true, false, true, true, false, false)) {
+ mListener.onDozeChanged(false /* isDozing */, v /* isAwake */);
+ }
+
+ assertThat(actual).containsExactly(true, false, true, false).inOrder();
}
@Test
@@ -124,13 +153,13 @@
mProvider.subscribe(mOpContext, emptyConsumer);
mProvider.unsubscribe(mOpContext);
- mListener.onDozeChanged(true);
+ mListener.onDozeChanged(true /* isDozing */, false /* isAwake */);
final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
mProvider.subscribe(mOpContext, nonEmptyConsumer);
- mListener.onDozeChanged(false);
+ mListener.onDozeChanged(false /* isDozing */, false /* isAwake */);
mProvider.unsubscribe(mOpContext);
- mListener.onDozeChanged(true);
+ mListener.onDozeChanged(true /* isDozing */, false /* isAwake */);
verify(emptyConsumer, never()).accept(any());
verify(nonEmptyConsumer).accept(same(mOpContext));
@@ -171,7 +200,7 @@
@Test
public void testUpdate() throws RemoteException {
- mListener.onDozeChanged(false);
+ mListener.onDozeChanged(false /* isDozing */, false /* isAwake */);
OperationContext context = mProvider.updateContext(mOpContext, false /* crypto */);
// default state when nothing has been set
@@ -186,7 +215,7 @@
final int id = 40 + type;
final boolean aod = (type & 1) == 0;
- mListener.onDozeChanged(aod);
+ mListener.onDozeChanged(aod /* isDozing */, false /* isAwake */);
mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id));
context = mProvider.updateContext(mOpContext, false /* crypto */);
assertThat(context).isSameInstanceAs(mOpContext);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 0b8e8ad..60dc2eb 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -232,7 +232,7 @@
public void testALSCallback() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
- mLogger.createALSCallback(true /* startWithClient */);
+ mLogger.getAmbientLightProbe(true /* startWithClient */);
callback.onClientStarted(mClient);
verify(mSensorManager).registerListener(any(), any(), anyInt());
@@ -242,10 +242,37 @@
}
@Test
+ public void testALSCallbackWhenLogsDisabled() {
+ mLogger = createLogger();
+ mLogger.disableMetrics();
+ final CallbackWithProbe<Probe> callback =
+ mLogger.getAmbientLightProbe(true /* startWithClient */);
+
+ callback.onClientStarted(mClient);
+ verify(mSensorManager, never()).registerListener(any(), any(), anyInt());
+
+ callback.onClientFinished(mClient, true /* success */);
+ verify(mSensorManager, never()).unregisterListener(any(SensorEventListener.class));
+ }
+
+ @Test
+ public void testALSCallbackWhenDisabledAfterStarting() {
+ mLogger = createLogger();
+ final CallbackWithProbe<Probe> callback =
+ mLogger.getAmbientLightProbe(true /* startWithClient */);
+
+ callback.onClientStarted(mClient);
+ verify(mSensorManager).registerListener(any(), any(), anyInt());
+
+ mLogger.disableMetrics();
+ verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+ }
+
+ @Test
public void testALSCallbackDoesNotStart() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
- mLogger.createALSCallback(false /* startWithClient */);
+ mLogger.getAmbientLightProbe(false /* startWithClient */);
callback.onClientStarted(mClient);
callback.onClientFinished(mClient, true /* success */);
@@ -256,7 +283,7 @@
public void testALSCallbackDestroyed() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
- mLogger.createALSCallback(true /* startWithClient */);
+ mLogger.getAmbientLightProbe(true /* startWithClient */);
callback.onClientStarted(mClient);
callback.onClientFinished(mClient, false /* success */);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 45e3b43..eb131419 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -91,8 +91,7 @@
mToken = new Binder();
mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
- mBiometricService, LOG_NUM_RECENT_OPERATIONS,
- CoexCoordinator.getInstance());
+ mBiometricService, LOG_NUM_RECENT_OPERATIONS);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
deleted file mode 100644
index abf992b..0000000
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Copyright (C) 2021 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.server.biometrics.sensors;
-
-import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_FACE;
-import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_FP_OTHER;
-import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_UDFPS;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.hardware.biometrics.BiometricConstants;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.biometrics.sensors.fingerprint.Udfps;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.LinkedList;
-
-@Presubmit
-@SmallTest
-public class CoexCoordinatorTest {
-
- @Rule
- public final MockitoRule mockito = MockitoJUnit.rule();
-
- @Mock
- private CoexCoordinator.Callback mCallback;
- @Mock
- private CoexCoordinator.ErrorCallback mErrorCallback;
- @Mock
- private AuthenticationClient mFaceClient;
- @Mock
- private AuthenticationClient mFingerprintClient;
- @Mock(extraInterfaces = {Udfps.class})
- private AuthenticationClient mUdfpsClient;
-
- private CoexCoordinator mCoexCoordinator;
-
- @Before
- public void setUp() {
- mCoexCoordinator = CoexCoordinator.getInstance();
- mCoexCoordinator.setAdvancedLogicEnabled(true);
- mCoexCoordinator.setFaceHapticDisabledWhenNonBypass(true);
- mCoexCoordinator.reset();
- }
-
- @Test
- public void testBiometricPrompt_authSuccess() {
- when(mFaceClient.isBiometricPrompt()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
- mFaceClient, mCallback);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testBiometricPrompt_authReject_whenNotLockedOut() {
- when(mFaceClient.isBiometricPrompt()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- mFaceClient, LockoutTracker.LOCKOUT_NONE, mCallback);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testBiometricPrompt_authReject_whenLockedOut() {
- when(mFaceClient.isBiometricPrompt()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- mFaceClient, LockoutTracker.LOCKOUT_TIMED, mCallback);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testBiometricPrompt_coex_success() {
- testBiometricPrompt_coex_success(false /* twice */);
- }
-
- @Test
- public void testBiometricPrompt_coex_successWithoutDouble() {
- testBiometricPrompt_coex_success(true /* twice */);
- }
-
- private void testBiometricPrompt_coex_success(boolean twice) {
- initFaceAndFingerprintForBiometricPrompt();
- when(mFaceClient.wasAuthSuccessful()).thenReturn(true);
- when(mUdfpsClient.wasAuthSuccessful()).thenReturn(twice, true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
- mFaceClient, mCallback);
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
- mUdfpsClient, mCallback);
-
- if (twice) {
- verify(mCallback, never()).sendHapticFeedback();
- } else {
- verify(mCallback).sendHapticFeedback();
- }
- }
-
- @Test
- public void testBiometricPrompt_coex_reject() {
- initFaceAndFingerprintForBiometricPrompt();
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- mFaceClient, LockoutTracker.LOCKOUT_NONE, mCallback);
-
- verify(mCallback, never()).sendHapticFeedback();
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- mUdfpsClient, LockoutTracker.LOCKOUT_NONE, mCallback);
-
- verify(mCallback).sendHapticFeedback();
- }
-
- @Test
- public void testBiometricPrompt_coex_errorNoHaptics() {
- initFaceAndFingerprintForBiometricPrompt();
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationError(mFaceClient,
- BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
- mCoexCoordinator.onAuthenticationError(mUdfpsClient,
- BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
-
- verify(mErrorCallback, never()).sendHapticFeedback();
- }
-
- private void initFaceAndFingerprintForBiometricPrompt() {
- when(mFaceClient.isKeyguard()).thenReturn(false);
- when(mFaceClient.isBiometricPrompt()).thenReturn(true);
- when(mFaceClient.wasAuthAttempted()).thenReturn(true);
- when(mUdfpsClient.isKeyguard()).thenReturn(false);
- when(mUdfpsClient.isBiometricPrompt()).thenReturn(true);
- when(mUdfpsClient.wasAuthAttempted()).thenReturn(true);
- }
-
- @Test
- public void testKeyguard_faceAuthOnly_success() {
- when(mFaceClient.isKeyguard()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
- mFaceClient, mCallback);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testKeyguard_faceAuth_udfpsNotTouching_faceSuccess() {
- when(mFaceClient.isKeyguard()).thenReturn(true);
-
- when(mUdfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(false);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
- mFaceClient, mCallback);
- // Haptics tested in #testKeyguard_bypass_haptics. Let's leave this commented out (instead
- // of removed) to keep this context.
- // verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testKeyguard_faceAuthSuccess_nonBypass_udfpsRunning_noHaptics() {
- testKeyguard_bypass_haptics(false /* bypassEnabled */,
- true /* faceAccepted */,
- false /* shouldReceiveHaptics */);
- }
-
- @Test
- public void testKeyguard_faceAuthReject_nonBypass_udfpsRunning_noHaptics() {
- testKeyguard_bypass_haptics(false /* bypassEnabled */,
- false /* faceAccepted */,
- false /* shouldReceiveHaptics */);
- }
-
- @Test
- public void testKeyguard_faceAuthSuccess_bypass_udfpsRunning_haptics() {
- testKeyguard_bypass_haptics(true /* bypassEnabled */,
- true /* faceAccepted */,
- true /* shouldReceiveHaptics */);
- }
-
- @Test
- public void testKeyguard_faceAuthReject_bypass_udfpsRunning_haptics() {
- testKeyguard_bypass_haptics(true /* bypassEnabled */,
- false /* faceAccepted */,
- true /* shouldReceiveHaptics */);
- }
-
- private void testKeyguard_bypass_haptics(boolean bypassEnabled, boolean faceAccepted,
- boolean shouldReceiveHaptics) {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
- when(mUdfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(false);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- if (faceAccepted) {
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mFaceClient,
- mCallback);
- } else {
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
- LockoutTracker.LOCKOUT_NONE, mCallback);
- }
-
- if (shouldReceiveHaptics) {
- verify(mCallback).sendHapticFeedback();
- } else {
- verify(mCallback, never()).sendHapticFeedback();
- }
-
- verify(mCallback).sendAuthenticationResult(eq(faceAccepted) /* addAuthTokenIfStrong */);
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testKeyguard_faceAuth_udfpsTouching_faceSuccess_thenUdfpsRejectedWithinBounds() {
- testKeyguard_faceAuth_udfpsTouching_faceSuccess(false /* thenUdfpsAccepted */,
- 0 /* udfpsRejectedAfterMs */);
- }
-
- @Test
- public void testKeyguard_faceAuth_udfpsTouching_faceSuccess_thenUdfpsRejectedAfterBounds() {
- testKeyguard_faceAuth_udfpsTouching_faceSuccess(false /* thenUdfpsAccepted */,
- CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS + 1 /* udfpsRejectedAfterMs */);
- }
-
- @Test
- public void testKeyguard_faceAuth_udfpsTouching_faceSuccess_thenUdfpsAccepted() {
- testKeyguard_faceAuth_udfpsTouching_faceSuccess(true /* thenUdfpsAccepted */,
- 0 /* udfpsRejectedAfterMs */);
- }
-
- private void testKeyguard_faceAuth_udfpsTouching_faceSuccess(boolean thenUdfpsAccepted,
- long udfpsRejectedAfterMs) {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mUdfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
- when(mUdfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- // For easier reading
- final CoexCoordinator.Callback faceCallback = mCallback;
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mFaceClient,
- faceCallback);
- verify(faceCallback, never()).sendHapticFeedback();
- verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
- // CoexCoordinator requests the system to hold onto this AuthenticationClient until
- // UDFPS result is known
- verify(faceCallback, never()).handleLifecycleAfterAuth();
-
- // Reset the mock
- CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
- assertEquals(1, mCoexCoordinator.mSuccessfulAuths.size());
- assertEquals(mFaceClient, mCoexCoordinator.mSuccessfulAuths.get(0).mAuthenticationClient);
- if (thenUdfpsAccepted) {
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mUdfpsClient,
- udfpsCallback);
- verify(udfpsCallback).sendHapticFeedback();
- verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */);
- verify(udfpsCallback).handleLifecycleAfterAuth();
-
- verify(faceCallback).sendAuthenticationCanceled();
-
- assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
- } else {
- mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, mUdfpsClient,
- LockoutTracker.LOCKOUT_NONE, udfpsCallback);
- if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) {
- verify(udfpsCallback, never()).sendHapticFeedback();
-
- verify(faceCallback).sendHapticFeedback();
- verify(faceCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
- verify(faceCallback).handleLifecycleAfterAuth();
-
- assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
- } else {
- assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
-
- verify(faceCallback, never()).sendHapticFeedback();
- verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
-
- verify(udfpsCallback).sendHapticFeedback();
- verify(udfpsCallback)
- .sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
- verify(udfpsCallback).handleLifecycleAfterAuth();
- }
- }
- }
-
- @Test
- public void testKeyguard_udfpsAuthSuccess_whileFaceScanning() {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mUdfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mUdfpsClient,
- mCallback);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(true));
- verify(mFaceClient).cancel();
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testKeyguard_faceRejectedWhenUdfpsTouching_thenUdfpsRejected() {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mUdfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mUdfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
- LockoutTracker.LOCKOUT_NONE, mCallback);
- verify(mCallback, never()).sendHapticFeedback();
- verify(mCallback).handleLifecycleAfterAuth();
-
- // BiometricScheduler removes the face authentication client after rejection
- mCoexCoordinator.removeAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
-
- // Then UDFPS rejected
- CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
- mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, mUdfpsClient,
- LockoutTracker.LOCKOUT_NONE, udfpsCallback);
- verify(udfpsCallback).sendHapticFeedback();
- verify(udfpsCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
- verify(mCallback, never()).sendHapticFeedback();
- }
-
- @Test
- public void testKeyguard_udfpsRejected_thenFaceRejected_noKeyguardBypass() {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(false); // TODO: also test "true" case
- when(mUdfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mUdfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- mUdfpsClient, LockoutTracker.LOCKOUT_NONE, mCallback);
- // Auth was attempted
- when(mUdfpsClient.getState())
- .thenReturn(AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).handleLifecycleAfterAuth();
-
- // Then face rejected. Note that scheduler leaves UDFPS in the CoexCoordinator since
- // unlike face, its lifecycle becomes "paused" instead of "finished".
- CoexCoordinator.Callback faceCallback = mock(CoexCoordinator.Callback.class);
- mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, mFaceClient,
- LockoutTracker.LOCKOUT_NONE, faceCallback);
- verify(faceCallback).sendHapticFeedback();
- verify(faceCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
- verify(mCallback).sendHapticFeedback();
- }
-
- @Test
- public void testKeyguard_capacitiveAccepted_whenFaceScanning() {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mFingerprintClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mFingerprintClient.isKeyguard()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FP_OTHER, mFingerprintClient);
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
- mFingerprintClient, mCallback);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testKeyguard_capacitiveRejected_whenFaceScanning() {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mFingerprintClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(mFingerprintClient.isKeyguard()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FP_OTHER, mFingerprintClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- mFingerprintClient, LockoutTracker.LOCKOUT_NONE, mCallback);
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testNonKeyguard_rejectAndNotLockedOut() {
- when(mFaceClient.isKeyguard()).thenReturn(false);
- when(mFaceClient.isBiometricPrompt()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
- LockoutTracker.LOCKOUT_NONE, mCallback);
-
- verify(mCallback).sendHapticFeedback();
- verify(mCallback).sendAuthenticationResult(eq(false));
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testNonKeyguard_rejectLockedOut() {
- when(mFaceClient.isKeyguard()).thenReturn(false);
- when(mFaceClient.isBiometricPrompt()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
- LockoutTracker.LOCKOUT_TIMED, mCallback);
-
- verify(mCallback).sendHapticFeedback();
- verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
- verify(mCallback).handleLifecycleAfterAuth();
- }
-
- @Test
- public void testCleanupRunnable() {
- LinkedList<CoexCoordinator.SuccessfulAuth> successfulAuths = mock(LinkedList.class);
- CoexCoordinator.SuccessfulAuth auth = mock(CoexCoordinator.SuccessfulAuth.class);
- CoexCoordinator.Callback callback = mock(CoexCoordinator.Callback.class);
- CoexCoordinator.SuccessfulAuth.CleanupRunnable runnable =
- new CoexCoordinator.SuccessfulAuth.CleanupRunnable(successfulAuths, auth, callback);
- runnable.run();
-
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
- verify(callback).handleLifecycleAfterAuth();
- verify(successfulAuths).remove(eq(auth));
- }
-
- @Test
- public void testBiometricPrompt_FaceError() {
- when(mFaceClient.isBiometricPrompt()).thenReturn(true);
- when(mFaceClient.wasAuthAttempted()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
-
- mCoexCoordinator.onAuthenticationError(mFaceClient,
- BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
- verify(mErrorCallback).sendHapticFeedback();
- }
-
- @Test
- public void testKeyguard_faceAuthOnly_errorWhenBypassEnabled() {
- testKeyguard_faceAuthOnly(true /* bypassEnabled */);
- }
-
- @Test
- public void testKeyguard_faceAuthOnly_errorWhenBypassDisabled() {
- testKeyguard_faceAuthOnly(false /* bypassEnabled */);
- }
-
- private void testKeyguard_faceAuthOnly(boolean bypassEnabled) {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
- when(mFaceClient.wasAuthAttempted()).thenReturn(true);
- when(mFaceClient.wasUserDetected()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
-
- mCoexCoordinator.onAuthenticationError(mFaceClient,
- BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
- verify(mErrorCallback).sendHapticFeedback();
- }
-
- @Test
- public void testKeyguard_coex_faceErrorWhenBypassEnabled() {
- testKeyguard_coex_faceError(true /* bypassEnabled */);
- }
-
- @Test
- public void testKeyguard_coex_faceErrorWhenBypassDisabled() {
- testKeyguard_coex_faceError(false /* bypassEnabled */);
- }
-
- private void testKeyguard_coex_faceError(boolean bypassEnabled) {
- when(mFaceClient.isKeyguard()).thenReturn(true);
- when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
- when(mFaceClient.wasAuthAttempted()).thenReturn(true);
- when(mFaceClient.wasUserDetected()).thenReturn(true);
- when(mUdfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(false);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
-
- mCoexCoordinator.onAuthenticationError(mFaceClient,
- BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
-
- if (bypassEnabled) {
- verify(mErrorCallback).sendHapticFeedback();
- } else {
- verify(mErrorCallback, never()).sendHapticFeedback();
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 0df3028..0815fe5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -118,8 +118,7 @@
TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
mUserStartedCallback, mStartOperationsFinish);
}
- },
- CoexCoordinator.getInstance());
+ });
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index b60324e..518946a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -35,7 +35,6 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -91,8 +90,7 @@
null /* gestureAvailabilityDispatcher */,
mBiometricService,
() -> USER_ID,
- mUserSwitchCallback,
- CoexCoordinator.getInstance());
+ mUserSwitchCallback);
mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
TAG, mScheduler, SENSOR_ID,
USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index a149f3e..dea4d4f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -26,8 +26,8 @@
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.same;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -96,6 +96,7 @@
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+
@Mock
private ISession mHal;
@Mock
@@ -137,7 +138,7 @@
@Before
public void setup() {
mContext.addMockSystemService(BiometricManager.class, mBiometricManager);
- when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
+ when(mBiometricLogger.getAmbientLightProbe(anyBoolean())).thenAnswer(i ->
new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
i -> i.getArgument(0));
@@ -213,21 +214,41 @@
}
@Test
- public void luxProbeWhenFingerDown() throws RemoteException {
+ public void luxProbeWhenAwake() throws RemoteException {
+ when(mBiometricContext.isAwake()).thenReturn(false, true, false);
+ when(mBiometricContext.isAod()).thenReturn(false);
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
- client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
- verify(mLuxProbe).enable();
+ verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ OperationContext opContext = mOperationContextCaptor.getValue();
+ verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
- client.onAcquired(2, 0);
+ mContextInjector.getValue().accept(opContext);
+ verify(mLuxProbe, never()).enable();
+
+ reset(mLuxProbe);
+ mContextInjector.getValue().accept(opContext);
+ verify(mLuxProbe).enable();
verify(mLuxProbe, never()).disable();
- client.onPointerUp();
+ mContextInjector.getValue().accept(opContext);
verify(mLuxProbe).disable();
+ }
- client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
- verify(mLuxProbe, times(2)).enable();
+ @Test
+ public void luxProbeDisabledOnAod() throws RemoteException {
+ when(mBiometricContext.isAwake()).thenReturn(false);
+ when(mBiometricContext.isAod()).thenReturn(true);
+ final FingerprintAuthenticationClient client = createClient();
+ client.start(mCallback);
+
+ verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ OperationContext opContext = mOperationContextCaptor.getValue();
+ verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+
+ mContextInjector.getValue().accept(opContext);
+ verify(mLuxProbe, never()).enable();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index f77eb0b..92e1f27a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -64,6 +64,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
import java.util.function.Consumer;
@Presubmit
@@ -119,7 +120,7 @@
@Before
public void setup() {
- when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
+ when(mBiometricLogger.getAmbientLightProbe(anyBoolean())).thenAnswer(i ->
new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
i -> i.getArgument(0));
@@ -196,21 +197,22 @@
}
@Test
- public void luxProbeWhenFingerDown() throws RemoteException {
+ public void luxProbeWhenStarted() throws RemoteException {
final FingerprintEnrollClient client = createClient();
client.start(mCallback);
- client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
verify(mLuxProbe).enable();
client.onAcquired(2, 0);
- verify(mLuxProbe, never()).disable();
-
client.onPointerUp();
- verify(mLuxProbe).disable();
-
client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
- verify(mLuxProbe, times(2)).enable();
+ verify(mLuxProbe, never()).disable();
+ verify(mLuxProbe, never()).destroy();
+
+ client.onEnrollResult(new Fingerprint("f", 30 /* fingerId */, 14 /* deviceId */),
+ 0 /* remaining */);
+
+ verify(mLuxProbe).destroy();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index e1a4a2d..ff636c8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -35,7 +35,6 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -91,8 +90,7 @@
null /* gestureAvailabilityDispatcher */,
mBiometricService,
() -> USER_ID,
- mUserSwitchCallback,
- CoexCoordinator.getInstance());
+ mUserSwitchCallback);
mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
TAG, mScheduler, SENSOR_ID,
USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
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 261b882..03ea613 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -19,19 +19,16 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
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;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,16 +52,22 @@
private Resources mResources;
@Before
- public void setUp() {
+ public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
mockDeviceConfigs();
+ try {
+ Path tempFile = Files.createTempFile("display_config", ".tmp");
+ Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8));
+ mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
+ mDisplayDeviceConfig.initFromFile(tempFile.toFile());
+ } catch (IOException e) {
+ throw new IOException("Failed to setup the display device config.", e);
+ }
}
@Test
- public void testConfigValuesFromDisplayConfig() throws IOException {
- setupDisplayDeviceConfigFromDisplayConfigFile();
-
+ public void testConfigValues() {
assertEquals(mDisplayDeviceConfig.getAmbientHorizonLong(), 5000);
assertEquals(mDisplayDeviceConfig.getAmbientHorizonShort(), 50);
assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
@@ -85,24 +88,10 @@
assertEquals(mDisplayDeviceConfig.getScreenDarkeningMinThreshold(), 0.002, 0.000001f);
assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{50.0f, 80.0f}, 0.0f);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
- float[]{45.0f, 75.0f}, 0.0f);
+
// Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
- }
-
- @Test
- public void testConfigValuesFromDeviceConfig() {
- setupDisplayDeviceConfigFromDeviceConfigFile();
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 110.0f, 500.0f}, 0.0f);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
- float[]{2.0f, 200.0f, 600.0f}, 0.0f);
- // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
- // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
-
+ // Also add test for the case where optional display configs are null
}
private String getContent() {
@@ -125,16 +114,6 @@
+ "<autoBrightness>\n"
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
- + "<displayBrightnessMapping>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>50</lux>\n"
- + "<nits>45</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>80</lux>\n"
- + "<nits>75</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "</displayBrightnessMapping>\n"
+ "</autoBrightness>\n"
+ "<highBrightnessMode enabled=\"true\">\n"
+ "<transitionPoint>0.62</transitionPoint>\n"
@@ -206,64 +185,4 @@
when(mResources.getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingMaximumFloat)).thenReturn(1.0f);
}
-
- private void setupDisplayDeviceConfigFromDisplayConfigFile() throws IOException {
- Path tempFile = Files.createTempFile("display_config", ".tmp");
- Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8));
- mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
- mDisplayDeviceConfig.initFromFile(tempFile.toFile());
- }
-
- private void setupDisplayDeviceConfigFromDeviceConfigFile() {
- TypedArray screenBrightnessNits = createFloatTypedArray(new float[]{2.0f, 250.0f, 650.0f});
- when(mResources.obtainTypedArray(
- com.android.internal.R.array.config_screenBrightnessNits))
- .thenReturn(screenBrightnessNits);
- TypedArray screenBrightnessBacklight = createFloatTypedArray(new
- float[]{0.0f, 120.0f, 255.0f});
- when(mResources.obtainTypedArray(
- com.android.internal.R.array.config_screenBrightnessBacklight))
- .thenReturn(screenBrightnessBacklight);
- when(mResources.getIntArray(com.android.internal.R.array
- .config_screenBrightnessBacklight)).thenReturn(new int[]{0, 120, 255});
-
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessLevels)).thenReturn(new int[]{30, 80});
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessDisplayValuesNits)).thenReturn(new int[]{25, 55});
-
- TypedArray screenBrightnessLevelNits = createFloatTypedArray(new
- float[]{2.0f, 200.0f, 600.0f});
- when(mResources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
- .thenReturn(screenBrightnessLevelNits);
- TypedArray screenBrightnessLevelLux = createFloatTypedArray(new
- float[]{0.0f, 110.0f, 500.0f});
- when(mResources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessLevels))
- .thenReturn(screenBrightnessLevelLux);
-
- mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
-
- }
-
- private TypedArray createFloatTypedArray(float[] vals) {
- TypedArray mockArray = mock(TypedArray.class);
- when(mockArray.length()).thenAnswer(invocation -> {
- return vals.length;
- });
- when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> {
- final float def = (float) invocation.getArguments()[1];
- if (vals == null) {
- return def;
- }
- int idx = (int) invocation.getArguments()[0];
- if (idx >= 0 && idx < vals.length) {
- return vals[idx];
- } else {
- return def;
- }
- });
- return mockArray;
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java
index f330017..6a27f39 100644
--- a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.logcat;
+import static android.os.Process.INVALID_UID;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,6 +30,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
import android.os.ILogd;
import android.os.Looper;
import android.os.UserHandle;
@@ -69,6 +72,8 @@
@Mock
private ActivityManagerInternal mActivityManagerInternalMock;
@Mock
+ private PackageManager mPackageManagerMock;
+ @Mock
private ILogd mLogdMock;
private LogcatManagerService mService;
@@ -81,10 +86,17 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+ when(mActivityManagerInternalMock.getInstrumentationSourceUid(anyInt()))
+ .thenReturn(INVALID_UID);
+
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
-
+ when(mContextSpy.getPackageManager()).thenReturn(mPackageManagerMock);
+ when(mPackageManagerMock.getPackagesForUid(APP1_UID)).thenReturn(
+ new String[]{APP1_PACKAGE_NAME});
+ when(mPackageManagerMock.getPackagesForUid(APP2_UID)).thenReturn(
+ new String[]{APP2_PACKAGE_NAME});
when(mActivityManagerInternalMock.getPackageNameByPid(APP1_PID)).thenReturn(
APP1_PACKAGE_NAME);
when(mActivityManagerInternalMock.getPackageNameByPid(APP2_PID)).thenReturn(
@@ -136,6 +148,20 @@
}
@Test
+ public void test_RequestFromBackground_ApprovedIfInstrumented() throws Exception {
+ when(mActivityManagerInternalMock.getInstrumentationSourceUid(APP1_UID))
+ .thenReturn(APP1_UID);
+ when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn(
+ ActivityManager.PROCESS_STATE_RECEIVER);
+ mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1);
+ mTestLooper.dispatchAll();
+
+ verify(mLogdMock).approve(APP1_UID, APP1_GID, APP1_PID, FD1);
+ verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD1);
+ verify(mContextSpy, never()).startActivityAsUser(any(), any());
+ }
+
+ @Test
public void test_RequestFromForegroundService_DeclinedWithoutPrompt() throws Exception {
when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn(
ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
new file mode 100644
index 0000000..62875e5
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.policy;
+
+import static android.view.KeyEvent.KEYCODE_POWER;
+import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+
+import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS;
+import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
+
+import android.view.ViewConfiguration;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for combination key shortcuts.
+ *
+ * Build/Install/Run:
+ * atest WmTests:CombinationKeyTests
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class CombinationKeyTests extends ShortcutKeyTestBase {
+ private static final long A11Y_KEY_HOLD_MILLIS = 3500;
+
+ /**
+ * Power-VolDown to take screenshot.
+ */
+ @Test
+ public void testPowerVolumeDown() {
+ sendKeyCombination(new int[]{KEYCODE_POWER, KEYCODE_VOLUME_DOWN},
+ ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
+ mPhoneWindowManager.assertTakeScreenshotCalled();
+ }
+
+ /**
+ * Power-VolUp to show global actions or mute audio. (Phone default behavior)
+ */
+ @Test
+ public void testPowerVolumeUp() {
+ // Show global actions.
+ mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
+ sendKeyCombination(new int[]{KEYCODE_POWER, KEYCODE_VOLUME_UP}, 0);
+ mPhoneWindowManager.assertShowGlobalActionsCalled();
+
+ // Mute audio (hold over 100ms).
+ mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
+ sendKeyCombination(new int[]{KEYCODE_POWER, KEYCODE_VOLUME_UP}, 100);
+ mPhoneWindowManager.assertVolumeMute();
+ }
+
+ /**
+ * VolDown-VolUp and hold 3 secs to enable accessibility service.
+ */
+ @Test
+ public void testVolumeDownVolumeUp() {
+ sendKeyCombination(new int[]{KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP}, A11Y_KEY_HOLD_MILLIS);
+ mPhoneWindowManager.assertAccessibilityKeychordCalled();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
similarity index 98%
rename from services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java
rename to services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
index cf57181..4c69857 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
@@ -43,11 +43,11 @@
* Test class for {@link KeyCombinationManager}.
*
* Build/Install/Run:
- * atest KeyCombinationTests
+ * atest KeyCombinationManagerTests
*/
@SmallTest
-public class KeyCombinationTests {
+public class KeyCombinationManagerTests {
private KeyCombinationManager mKeyCombinationManager;
private final CountDownLatch mAction1Triggered = new CountDownLatch(1);
@@ -228,4 +228,4 @@
mKeyCombinationManager.interceptKey(firstKeyDown, true);
assertTrue(mKeyCombinationManager.getKeyInterceptTimeout(KEYCODE_VOLUME_UP) > eventTime);
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
new file mode 100644
index 0000000..7c6fd05
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -0,0 +1,84 @@
+/*
+ * 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.server.policy;
+
+import static android.view.KeyEvent.KEYCODE_POWER;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS;
+
+import android.view.Display;
+
+import org.junit.Test;
+
+/**
+ * Test class for power key gesture.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PowerKeyGestureTests
+ */
+public class PowerKeyGestureTests extends ShortcutKeyTestBase {
+ /**
+ * Power single press to turn screen on/off.
+ */
+ @Test
+ public void testPowerSinglePress() {
+ sendKey(KEYCODE_POWER);
+ mPhoneWindowManager.assertPowerSleep();
+
+ // turn screen on when begin from non-interactive.
+ mPhoneWindowManager.overrideDisplayState(Display.STATE_OFF);
+ sendKey(KEYCODE_POWER);
+ mPhoneWindowManager.assertPowerWakeUp();
+ mPhoneWindowManager.assertNoPowerSleep();
+ }
+
+ /**
+ * Power double press to trigger camera.
+ */
+ @Test
+ public void testPowerDoublePress() {
+ sendKey(KEYCODE_POWER);
+ sendKey(KEYCODE_POWER);
+ mPhoneWindowManager.assertCameraLaunch();
+ }
+
+ /**
+ * Power long press to show assistant or global actions.
+ */
+ @Test
+ public void testPowerLongPress() {
+ // Show assistant.
+ mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_ASSISTANT);
+ sendKey(KEYCODE_POWER, true);
+ mPhoneWindowManager.assertAssistLaunch();
+
+ // Show global actions.
+ mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_GLOBAL_ACTIONS);
+ sendKey(KEYCODE_POWER, true);
+ mPhoneWindowManager.assertShowGlobalActionsCalled();
+ }
+
+ /**
+ * Ignore power press if combination key already triggered.
+ */
+ @Test
+ public void testIgnoreSinglePressWhenCombinationKeyTriggered() {
+ sendKeyCombination(new int[]{KEYCODE_POWER, KEYCODE_VOLUME_UP}, 0);
+ mPhoneWindowManager.assertNoPowerSleep();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
new file mode 100644
index 0000000..6368f47
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -0,0 +1,169 @@
+/*
+ * 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.server.policy;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.KeyEvent.KEYCODE_ALT_LEFT;
+import static android.view.KeyEvent.KEYCODE_ALT_RIGHT;
+import static android.view.KeyEvent.KEYCODE_CTRL_LEFT;
+import static android.view.KeyEvent.KEYCODE_CTRL_RIGHT;
+import static android.view.KeyEvent.KEYCODE_META_LEFT;
+import static android.view.KeyEvent.KEYCODE_META_RIGHT;
+import static android.view.KeyEvent.KEYCODE_SHIFT_LEFT;
+import static android.view.KeyEvent.KEYCODE_SHIFT_RIGHT;
+import static android.view.KeyEvent.META_ALT_LEFT_ON;
+import static android.view.KeyEvent.META_ALT_ON;
+import static android.view.KeyEvent.META_ALT_RIGHT_ON;
+import static android.view.KeyEvent.META_CTRL_LEFT_ON;
+import static android.view.KeyEvent.META_CTRL_ON;
+import static android.view.KeyEvent.META_CTRL_RIGHT_ON;
+import static android.view.KeyEvent.META_META_LEFT_ON;
+import static android.view.KeyEvent.META_META_ON;
+import static android.view.KeyEvent.META_META_RIGHT_ON;
+import static android.view.KeyEvent.META_SHIFT_LEFT_ON;
+import static android.view.KeyEvent.META_SHIFT_ON;
+import static android.view.KeyEvent.META_SHIFT_RIGHT_ON;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
+
+import static java.util.Collections.unmodifiableMap;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.Map;
+
+class ShortcutKeyTestBase {
+ TestPhoneWindowManager mPhoneWindowManager;
+ final Context mContext = getInstrumentation().getTargetContext();
+
+ /** Modifier key to meta state */
+ private static final Map<Integer, Integer> MODIFIER;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(KEYCODE_CTRL_LEFT, META_CTRL_LEFT_ON | META_CTRL_ON);
+ map.put(KEYCODE_CTRL_RIGHT, META_CTRL_RIGHT_ON | META_CTRL_ON);
+ map.put(KEYCODE_ALT_LEFT, META_ALT_LEFT_ON | META_ALT_ON);
+ map.put(KEYCODE_ALT_RIGHT, META_ALT_RIGHT_ON | META_ALT_ON);
+ map.put(KEYCODE_SHIFT_LEFT, META_SHIFT_LEFT_ON | META_SHIFT_ON);
+ map.put(KEYCODE_SHIFT_RIGHT, META_SHIFT_RIGHT_ON | META_SHIFT_ON);
+ map.put(KEYCODE_META_LEFT, META_META_LEFT_ON | META_META_ON);
+ map.put(KEYCODE_META_RIGHT, META_META_RIGHT_ON | META_META_ON);
+
+ MODIFIER = unmodifiableMap(map);
+ }
+
+ @Before
+ public void setUp() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mPhoneWindowManager = new TestPhoneWindowManager(mContext);
+ }
+
+ @After
+ public void tearDown() {
+ mPhoneWindowManager.tearDown();
+ }
+
+ void sendKeyCombination(int[] keyCodes, long duration) {
+ final long downTime = SystemClock.uptimeMillis();
+ final int count = keyCodes.length;
+ final KeyEvent[] events = new KeyEvent[count];
+ int metaState = 0;
+ for (int i = 0; i < count; i++) {
+ final int keyCode = keyCodes[i];
+ final KeyEvent event = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode,
+ 0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+ 0 /*flags*/, InputDevice.SOURCE_KEYBOARD);
+ event.setDisplayId(DEFAULT_DISPLAY);
+ events[i] = event;
+ // The order is important here, metaState could be updated and applied to the next key.
+ metaState |= MODIFIER.getOrDefault(keyCode, 0);
+ }
+
+ for (KeyEvent event: events) {
+ interceptKey(event);
+ }
+
+ try {
+ Thread.sleep(duration);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ for (KeyEvent event: events) {
+ final long eventTime = SystemClock.uptimeMillis();
+ final int keyCode = event.getKeyCode();
+ final KeyEvent upEvent = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP, keyCode,
+ 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
+ InputDevice.SOURCE_KEYBOARD);
+ interceptKey(upEvent);
+ metaState &= ~MODIFIER.getOrDefault(keyCode, 0);
+ }
+ }
+
+ void sendKey(int keyCode) {
+ sendKey(keyCode, false);
+ }
+
+ void sendKey(int keyCode, boolean longPress) {
+ final long downTime = SystemClock.uptimeMillis();
+ final KeyEvent event = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode,
+ 0 /*repeat*/, 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+ 0 /*flags*/, InputDevice.SOURCE_KEYBOARD);
+ event.setDisplayId(DEFAULT_DISPLAY);
+ interceptKey(event);
+
+ if (longPress) {
+ final long nextDownTime = downTime + ViewConfiguration.getLongPressTimeout();
+ final KeyEvent nextDownevent = new KeyEvent(downTime, nextDownTime,
+ KeyEvent.ACTION_DOWN, keyCode, 1 /*repeat*/, 0 /*metaState*/,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+ KeyEvent.FLAG_LONG_PRESS /*flags*/, InputDevice.SOURCE_KEYBOARD);
+ interceptKey(nextDownevent);
+ }
+
+ final long eventTime = longPress
+ ? SystemClock.uptimeMillis() + ViewConfiguration.getLongPressTimeout()
+ : SystemClock.uptimeMillis();
+ final KeyEvent upEvent = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP, keyCode,
+ 0 /*repeat*/, 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+ 0 /*flags*/, InputDevice.SOURCE_KEYBOARD);
+ interceptKey(upEvent);
+ }
+
+ private void interceptKey(KeyEvent keyEvent) {
+ int actions = mPhoneWindowManager.interceptKeyBeforeQueueing(keyEvent);
+ if ((actions & ACTION_PASS_TO_USER) != 0) {
+ if (0 == mPhoneWindowManager.interceptKeyBeforeDispatching(keyEvent)) {
+ mPhoneWindowManager.dispatchUnhandledKey(keyEvent);
+ }
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
new file mode 100644
index 0000000..9d57f48
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -0,0 +1,359 @@
+/*
+ * 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.server.policy;
+
+import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_ON;
+import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GO_TO_VOICE_ASSIST;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_NOTHING;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_SHUT_OFF;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
+import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.withSettings;
+
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.app.NotificationManager;
+import android.app.SearchManager;
+import android.content.Context;
+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;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.Vibrator;
+import android.service.dreams.DreamManagerInternal;
+import android.telecom.TelecomManager;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.autofill.AutofillManagerInternal;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.server.GestureLauncherService;
+import com.android.server.LocalServices;
+import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.DisplayPolicy;
+import com.android.server.wm.DisplayRotation;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.mockito.Mock;
+import org.mockito.MockSettings;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Supplier;
+
+class TestPhoneWindowManager {
+ private static final long SHORTCUT_KEY_DELAY_MILLIS = 150;
+
+ private PhoneWindowManager mPhoneWindowManager;
+ private Context mContext;
+
+ @Mock private WindowManagerInternal mWindowManagerInternal;
+ @Mock private ActivityManagerInternal mActivityManagerInternal;
+ @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ @Mock private InputManagerInternal mInputManagerInternal;
+ @Mock private DreamManagerInternal mDreamManagerInternal;
+ @Mock private PowerManagerInternal mPowerManagerInternal;
+ @Mock private DisplayManagerInternal mDisplayManagerInternal;
+ @Mock private AppOpsManager mAppOpsManager;
+ @Mock private DisplayManager mDisplayManager;
+ @Mock private PackageManager mPackageManager;
+ @Mock private TelecomManager mTelecomManager;
+ @Mock private NotificationManager mNotificationManager;
+ @Mock private Vibrator mVibrator;
+ @Mock private PowerManager mPowerManager;
+ @Mock private WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncsImpl;
+ @Mock private AudioManagerInternal mAudioManagerInternal;
+ @Mock private SearchManager mSearchManager;
+
+ @Mock private Display mDisplay;
+ @Mock private DisplayRotation mDisplayRotation;
+ @Mock private DisplayPolicy mDisplayPolicy;
+ @Mock private WindowManagerPolicy.ScreenOnListener mScreenOnListener;
+ @Mock private GestureLauncherService mGestureLauncherService;
+ @Mock private GlobalActions mGlobalActions;
+ @Mock private AccessibilityShortcutController mAccessibilityShortcutController;
+
+ private StaticMockitoSession mMockitoSession;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+
+ private class TestInjector extends PhoneWindowManager.Injector {
+ TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
+ super(context, funcs);
+ }
+
+ AccessibilityShortcutController getAccessibilityShortcutController(
+ Context context, Handler handler, int initialUserId) {
+ return mAccessibilityShortcutController;
+ }
+
+ Supplier<GlobalActions> getGlobalActionsFactory() {
+ return () -> mGlobalActions;
+ }
+ }
+
+ TestPhoneWindowManager(Context context) {
+ MockitoAnnotations.initMocks(this);
+ mHandlerThread = new HandlerThread("fake window manager");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mHandler.runWithScissors(()-> setUp(context), 0 /* timeout */);
+ }
+
+ private void setUp(Context context) {
+ mPhoneWindowManager = spy(new PhoneWindowManager());
+ mContext = spy(context);
+
+ // Use stubOnly() to reduce memory usage if it doesn't need verification.
+ final MockSettings spyStubOnly = withSettings().stubOnly()
+ .defaultAnswer(CALLS_REAL_METHODS);
+ // Return mocked services: LocalServices.getService
+ mMockitoSession = mockitoSession()
+ .mockStatic(LocalServices.class, spyStubOnly)
+ .startMocking();
+
+ doReturn(mWindowManagerInternal).when(
+ () -> LocalServices.getService(eq(WindowManagerInternal.class)));
+ doReturn(mActivityManagerInternal).when(
+ () -> LocalServices.getService(eq(ActivityManagerInternal.class)));
+ doReturn(mActivityTaskManagerInternal).when(
+ () -> LocalServices.getService(eq(ActivityTaskManagerInternal.class)));
+ doReturn(mInputManagerInternal).when(
+ () -> LocalServices.getService(eq(InputManagerInternal.class)));
+ doReturn(mDreamManagerInternal).when(
+ () -> LocalServices.getService(eq(DreamManagerInternal.class)));
+ doReturn(mPowerManagerInternal).when(
+ () -> LocalServices.getService(eq(PowerManagerInternal.class)));
+ doReturn(mDisplayManagerInternal).when(
+ () -> LocalServices.getService(eq(DisplayManagerInternal.class)));
+ doReturn(mGestureLauncherService).when(
+ () -> LocalServices.getService(eq(GestureLauncherService.class)));
+ doReturn(null).when(() -> LocalServices.getService(eq(VrManagerInternal.class)));
+ doReturn(null).when(() -> LocalServices.getService(eq(AutofillManagerInternal.class)));
+
+ doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
+ doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(false).when(mPackageManager).hasSystemFeature(any());
+ try {
+ doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
+ .getActivityInfo(any(), anyInt());
+ doReturn(new String[] { "testPackage" }).when(mPackageManager)
+ .canonicalToCurrentPackageNames(any());
+ } catch (PackageManager.NameNotFoundException ignored) { }
+
+ doReturn(mTelecomManager).when(mContext).getSystemService(eq(Context.TELECOM_SERVICE));
+ doReturn(mNotificationManager).when(mContext)
+ .getSystemService(eq(NotificationManager.class));
+ doReturn(mVibrator).when(mContext).getSystemService(eq(Context.VIBRATOR_SERVICE));
+
+ final PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
+ doReturn(wakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+ doReturn(mPowerManager).when(mContext).getSystemService(eq(Context.POWER_SERVICE));
+ doReturn(true).when(mPowerManager).isInteractive();
+
+ doReturn(mDisplay).when(mDisplayManager).getDisplay(eq(DEFAULT_DISPLAY));
+ doReturn(STATE_ON).when(mDisplay).getState();
+ doReturn(true).when(mDisplayPolicy).isAwake();
+ doNothing().when(mDisplayPolicy).takeScreenshot(anyInt(), anyInt());
+ doReturn(mDisplayPolicy).when(mDisplayRotation).getDisplayPolicy();
+ doReturn(mScreenOnListener).when(mDisplayPolicy).getScreenOnListener();
+ mPhoneWindowManager.setDefaultDisplay(new WindowManagerPolicy.DisplayContentInfo() {
+ @Override
+ public DisplayRotation getDisplayRotation() {
+ return mDisplayRotation;
+ }
+ @Override
+ public Display getDisplay() {
+ return mDisplay;
+ }
+ });
+
+ doNothing().when(mPhoneWindowManager).initializeHdmiState();
+ doNothing().when(mPhoneWindowManager).updateSettings();
+ doNothing().when(mPhoneWindowManager).screenTurningOn(anyInt(), any());
+ doNothing().when(mPhoneWindowManager).screenTurnedOn(anyInt());
+ doNothing().when(mPhoneWindowManager).startedWakingUp(anyInt());
+ doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt());
+
+ mPhoneWindowManager.init(new TestInjector(mContext, mWindowManagerFuncsImpl));
+ mPhoneWindowManager.systemReady();
+ mPhoneWindowManager.systemBooted();
+
+ overrideLaunchAccessibility();
+ }
+
+ void tearDown() {
+ mHandlerThread.quitSafely();
+ mMockitoSession.finishMocking();
+ }
+
+ // Override accessibility setting and perform function.
+ private void overrideLaunchAccessibility() {
+ doReturn(true).when(mAccessibilityShortcutController)
+ .isAccessibilityShortcutAvailable(anyBoolean());
+ doNothing().when(mAccessibilityShortcutController).performAccessibilityShortcut();
+ }
+
+ int interceptKeyBeforeQueueing(KeyEvent event) {
+ return mPhoneWindowManager.interceptKeyBeforeQueueing(event, FLAG_INTERACTIVE);
+ }
+
+ long interceptKeyBeforeDispatching(KeyEvent event) {
+ return mPhoneWindowManager.interceptKeyBeforeDispatching(null /*focusedToken*/,
+ event, FLAG_INTERACTIVE);
+ }
+
+ void dispatchUnhandledKey(KeyEvent event) {
+ mPhoneWindowManager.dispatchUnhandledKey(null /*focusedToken*/, event, FLAG_INTERACTIVE);
+ }
+
+ void waitForIdle() {
+ mHandler.runWithScissors(() -> { }, 0 /* timeout */);
+ }
+
+ /**
+ * Below functions will override the setting or the policy behavior.
+ */
+ void overridePowerVolumeUp(int behavior) {
+ mPhoneWindowManager.mPowerVolUpBehavior = behavior;
+
+ // override mRingerToggleChord as mute so we could trigger the behavior.
+ if (behavior == POWER_VOLUME_UP_BEHAVIOR_MUTE) {
+ mPhoneWindowManager.mRingerToggleChord = VOLUME_HUSH_MUTE;
+ doReturn(mAudioManagerInternal).when(
+ () -> LocalServices.getService(eq(AudioManagerInternal.class)));
+ }
+ }
+
+ // Override assist perform function.
+ void overrideLongPressOnPower(int behavior) {
+ mPhoneWindowManager.mLongPressOnPowerBehavior = behavior;
+
+ switch (behavior) {
+ case LONG_PRESS_POWER_NOTHING:
+ case LONG_PRESS_POWER_GLOBAL_ACTIONS:
+ case LONG_PRESS_POWER_SHUT_OFF:
+ case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
+ case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
+ break;
+ case LONG_PRESS_POWER_ASSISTANT:
+ doNothing().when(mPhoneWindowManager).sendCloseSystemWindows();
+ doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+ doReturn(mSearchManager).when(mContext)
+ .getSystemService(eq(Context.SEARCH_SERVICE));
+ mPhoneWindowManager.mLongPressOnPowerAssistantTimeoutMs = 500;
+ break;
+ }
+ }
+
+ void overrideDisplayState(int state) {
+ doReturn(state).when(mDisplay).getState();
+ Mockito.reset(mPowerManager);
+ }
+
+ /**
+ * Below functions will check the policy behavior could be invoked.
+ */
+ void assertTakeScreenshotCalled() {
+ waitForIdle();
+ verify(mDisplayPolicy, timeout(SHORTCUT_KEY_DELAY_MILLIS))
+ .takeScreenshot(anyInt(), anyInt());
+ }
+
+ void assertShowGlobalActionsCalled() {
+ waitForIdle();
+ verify(mPhoneWindowManager).showGlobalActions();
+ verify(mGlobalActions, timeout(SHORTCUT_KEY_DELAY_MILLIS))
+ .showDialog(anyBoolean(), anyBoolean());
+ verify(mPowerManager, timeout(SHORTCUT_KEY_DELAY_MILLIS))
+ .userActivity(anyLong(), anyBoolean());
+ }
+
+ void assertVolumeMute() {
+ waitForIdle();
+ verify(mAudioManagerInternal, timeout(SHORTCUT_KEY_DELAY_MILLIS))
+ .silenceRingerModeInternal(eq("volume_hush"));
+ }
+
+ void assertAccessibilityKeychordCalled() {
+ waitForIdle();
+ verify(mAccessibilityShortcutController,
+ timeout(SHORTCUT_KEY_DELAY_MILLIS)).performAccessibilityShortcut();
+ }
+
+ void assertPowerSleep() {
+ waitForIdle();
+ verify(mPowerManager,
+ timeout(SHORTCUT_KEY_DELAY_MILLIS)).goToSleep(anyLong(), anyInt(), anyInt());
+ }
+
+ void assertPowerWakeUp() {
+ waitForIdle();
+ verify(mPowerManager,
+ timeout(SHORTCUT_KEY_DELAY_MILLIS)).wakeUp(anyLong(), anyInt(), anyString());
+ }
+
+ void assertNoPowerSleep() {
+ waitForIdle();
+ verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
+ }
+
+ void assertCameraLaunch() {
+ waitForIdle();
+ // GestureLauncherService should receive interceptPowerKeyDown twice.
+ verify(mGestureLauncherService, times(2))
+ .interceptPowerKeyDown(any(), anyBoolean(), any());
+ }
+
+ void assertAssistLaunch() {
+ waitForIdle();
+ verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any());
+ }
+}
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 4449483..25c8f14 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2869,6 +2869,7 @@
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
fragmentSetup.accept(taskFragment1, new Rect(0, 0, width / 2, height));
task.addChild(taskFragment1, POSITION_TOP);
+ assertEquals(task, activity1.mStartingData.mAssociatedTask);
final TaskFragment taskFragment2 = new TaskFragment(
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
@@ -2890,7 +2891,6 @@
eq(task.mSurfaceControl));
assertEquals(activity1.mStartingData, startingWindow.mStartingData);
assertEquals(task.mSurfaceControl, startingWindow.getAnimationLeashParent());
- assertEquals(task, activity1.mStartingData.mAssociatedTask);
assertEquals(taskFragment1.getBounds(), activity1.getBounds());
// The activity was resized by task fragment, but starting window must still cover the task.
assertEquals(taskBounds, activity1.mStartingWindow.getBounds());
@@ -2898,7 +2898,6 @@
// The starting window is only removed when all embedded activities are drawn.
final WindowState activityWindow = mock(WindowState.class);
activity1.onFirstWindowDrawn(activityWindow);
- assertNotNull(activity1.mStartingWindow);
activity2.onFirstWindowDrawn(activityWindow);
assertNull(activity1.mStartingWindow);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6d33aaf..1738706 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1640,6 +1640,75 @@
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
+ public void testOverrideMinAspectRatioExcludePortraitFullscreen() {
+ setUpDisplaySizeWithApp(2600, 1600);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // Non-resizable portrait activity
+ prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ // At first, OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply, because the
+ // display is in landscape
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+
+ rotateDisplay(activity.mDisplayContent, ROTATION_90);
+ prepareUnresizable(activity, /* maxAspect */ 0, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Now the display is in portrait fullscreen, so the override is applied making the content
+ // fullscreen
+ assertEquals(activity.getBounds(), activity.mDisplayContent.getBounds());
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
+ public void testOverrideMinAspectRatioExcludePortraitFullscreenNotApplied() {
+ // In this test, the activity is not in fullscreen, so the override is not applied
+ setUpDisplaySizeWithApp(2600, 1600);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, activity.getDisplayContent());
+
+ // Move first activity to split screen which takes half of the screen.
+ organizer.mPrimary.setBounds(0, 0, 1300, 1600);
+ organizer.putTaskToPrimary(mTask, true);
+
+ // Non-resizable portrait activity
+ prepareUnresizable(activity, /* maxAspect */ 0, SCREEN_ORIENTATION_PORTRAIT);
+
+ // OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply here because the
+ // display is not in fullscreen, so OVERRIDE_MIN_ASPECT_RATIO_LARGE applies instead
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
public void testSplitAspectRatioForUnresizableLandscapeApps() {
// Set up a display in portrait and ignoring orientation request.
int screenWidth = 1400;
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index f169926..1b34b81 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -772,7 +772,7 @@
observerApp.appUsageGroups.append(observerId, group);
if (DEBUG) {
- Slog.d(TAG, "addObserver " + observed + " for " + timeLimit);
+ Slog.d(TAG, "addObserver " + Arrays.toString(observed) + " for " + timeLimit);
}
user.addUsageGroup(group);
@@ -881,7 +881,7 @@
observerApp.appUsageLimitGroups.append(observerId, group);
if (DEBUG) {
- Slog.d(TAG, "addObserver " + observed + " for " + timeLimit);
+ Slog.d(TAG, "addObserver " + Arrays.toString(observed) + " for " + timeLimit);
}
user.addUsageGroup(group);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9fcf44e..ccec67e 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -15,6 +15,7 @@
package android.telecom;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import android.Manifest;
import android.annotation.IntDef;
@@ -2417,6 +2418,10 @@
if (service != null) {
try {
result = service.createManageBlockedNumbersIntent(mContext.getPackageName());
+ if (result != null) {
+ result.prepareToEnterProcess(LOCAL_FLAG_FROM_SYSTEM,
+ mContext.getAttributionSource());
+ }
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#createManageBlockedNumbersIntent", e);
}
@@ -2438,7 +2443,12 @@
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.createLaunchEmergencyDialerIntent(number);
+ Intent result = service.createLaunchEmergencyDialerIntent(number);
+ if (result != null) {
+ result.prepareToEnterProcess(LOCAL_FLAG_FROM_SYSTEM,
+ mContext.getAttributionSource());
+ }
+ return result;
} catch (RemoteException e) {
Log.e(TAG, "Error createLaunchEmergencyDialerIntent", e);
}
diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java
index 677fe2f..62eac7a 100755
--- a/telephony/common/com/google/android/mms/pdu/PduParser.java
+++ b/telephony/common/com/google/android/mms/pdu/PduParser.java
@@ -793,7 +793,7 @@
try {
if (LOCAL_LOGV) {
Log.v(LOG_TAG, "parseHeaders: CONTENT_TYPE: " + headerField +
- contentType.toString());
+ Arrays.toString(contentType));
}
headers.setTextString(contentType, PduHeaders.CONTENT_TYPE);
} catch(NullPointerException e) {
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index d978f57..212aaae 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -433,7 +433,8 @@
return Objects.hash(
mCellConnectionStatus, mCellBandwidthDownlinkKhz, mCellBandwidthUplinkKhz,
mNetworkType, mFrequencyRange, mDownlinkChannelNumber, mUplinkChannelNumber,
- mContextIds, mPhysicalCellId, mBand, mDownlinkFrequency, mUplinkFrequency);
+ Arrays.hashCode(mContextIds), mPhysicalCellId, mBand, mDownlinkFrequency,
+ mUplinkFrequency);
}
public static final
diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index ae7d209..3c18245 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -574,8 +574,8 @@
@Override
public int hashCode() {
- return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb, mThresholds,
- mIsEnabled);
+ return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb,
+ Arrays.hashCode(mThresholds), mIsEnabled);
}
public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR =
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index cbd03c7..d670e55 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1935,7 +1935,7 @@
+ " at calling enableCellBroadcastRangeForSubscriber. subId = " + subId);
}
} catch (RemoteException ex) {
- Rlog.d(TAG, "enableCellBroadcastRange: " + ex.getStackTrace());
+ Rlog.d(TAG, "enableCellBroadcastRange: ", ex);
// ignore it
}
@@ -1996,7 +1996,7 @@
+ " at calling disableCellBroadcastRangeForSubscriber. subId = " + subId);
}
} catch (RemoteException ex) {
- Rlog.d(TAG, "disableCellBroadcastRange: " + ex.getStackTrace());
+ Rlog.d(TAG, "disableCellBroadcastRange: ", ex);
// ignore it
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index c36eb2f..cb985bf 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -967,9 +967,9 @@
public int hashCode() {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
- mCardId, mDisplayName, mCarrierName, mNativeAccessRules, mIsGroupDisabled,
- mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex,
- mUsageSetting);
+ mCardId, mDisplayName, mCarrierName, Arrays.hashCode(mNativeAccessRules),
+ mIsGroupDisabled, mCarrierId, mProfileClass, mGroupOwner,
+ mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting);
}
@Override
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index e0c5298..19f2a9b 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -171,7 +171,7 @@
ci[i] = (CellInfo) parcelables[i];
}
executor.execute(() -> {
- Rlog.d(TAG, "onResults: " + ci.toString());
+ Rlog.d(TAG, "onResults: " + Arrays.toString(ci));
callback.onResults(Arrays.asList(ci));
});
} catch (Exception e) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt
index 3853af2..1a40f82 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt
@@ -164,7 +164,6 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 1,
supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 9cc1bfe..ec2b4fa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -95,7 +95,7 @@
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 58a8011..55d4129 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -103,7 +103,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index f6f3f58..725c10a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -113,8 +113,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- // b/190352379 (IME doesn't show on app launch in 90 degrees)
+ // b/190352379 (IME doesn't show on app launch in 90 degrees)
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 52f561e..8832686 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -123,7 +123,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3,
+ .getConfigNonRotationTests(
// b/190352379 (IME doesn't show on app launch in 90 degrees)
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
index c6e25d3..71e0aa1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
@@ -141,7 +141,6 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 2,
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 23bd220..0f91fd5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -121,7 +121,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 8ce1840..007a4f1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -134,8 +134,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index a04a50f..216e0eda 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -127,14 +127,13 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
+ .getConfigNonRotationTests(
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
)
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
index 04e4bc9..868290e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -141,8 +141,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
index b10aed3..16c23b9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
@@ -82,8 +82,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
index d900815..e587492 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
@@ -117,8 +117,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_90),
+ supportedRotations = listOf(Surface.ROTATION_90),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index fdc2193..c1f17f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -95,8 +95,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
index 9475734..5fd9442 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
@@ -265,7 +265,6 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 1,
supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 2e22e62..0281a60 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -192,8 +192,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0)
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 4f47ec4..85bf6d7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -200,8 +200,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
+ supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
supportedRotations = listOf(Surface.ROTATION_0)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 33c280e..eb9acc4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -142,7 +142,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index bfc7b39..b3db5b70 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -171,7 +171,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index f93d7a0..8c1d244 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -221,7 +221,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
index 75311ea..caf2e2d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -127,7 +127,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
index dbe5418..e744d44 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
@@ -157,7 +157,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 915b702..4ea4243 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -284,7 +284,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 7c07ace..a3dd0cb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -130,7 +130,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 53be7d4..82e30ac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -233,8 +233,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes =
+ supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY),
supportedRotations = listOf(Surface.ROTATION_0)
)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index fe5e74b..5f342a0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -263,7 +263,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 181767b..f85bad3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -278,8 +278,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
+ supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 0f05622..f6392ca 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -298,8 +298,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
+ supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index d1f356c..a714111 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -324,8 +324,7 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
+ supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
// TODO: Test with 90 rotation
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 4be8963..e6c1eac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -146,7 +146,7 @@
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigRotationTests(repetitions = 3)
+ .getConfigRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 0912812..07c2130 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -241,7 +241,7 @@
@JvmStatic
private fun getConfigurations(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigRotationTests(repetitions = 2)
+ .getConfigRotationTests()
.flatMap { sourceConfig ->
val defaultRun = createConfig(sourceConfig, starveUiThread = false)
val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
index 8b69db7..dc34cb6 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
@@ -15,21 +15,25 @@
*/
package com.google.android.test.handwritingime;
+import android.R;
import android.annotation.Nullable;
import android.graphics.PointF;
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
-import android.os.Bundle;
import android.util.Log;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
-import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.DeleteGesture;
+import android.view.inputmethod.HandwritingGesture;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.SelectGesture;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.Toast;
@@ -39,19 +43,19 @@
public static final int HEIGHT_DP = 100;
-
private static final int OP_NONE = 0;
private static final int OP_SELECT = 1;
private static final int OP_DELETE = 2;
- private static final int OP_DELETE_SPACE = 3;
- private static final int OP_INSERT = 4;
+ private static final int OP_INSERT = 3;
private Window mInkWindow;
private InkView mInk;
static final String TAG = "HandwritingIme";
private int mRichGestureMode = OP_NONE;
+ private int mRichGestureGranularity = -1;
private Spinner mRichGestureModeSpinner;
+ private Spinner mRichGestureGranularitySpinner;
private PointF mRichGestureStartPoint;
@@ -86,13 +90,45 @@
switch (event.getAction()) {
case MotionEvent.ACTION_UP: {
if (areRichGesturesEnabled()) {
- Bundle bundle = new Bundle();
- bundle.putInt("operation", mRichGestureMode);
- bundle.putFloat("left", mRichGestureStartPoint.x);
- bundle.putFloat("top", mRichGestureStartPoint.y);
- bundle.putFloat("right", event.getX());
- bundle.putFloat("bottom", event.getY());
- performPrivateCommand("android.widget.RichGesture", bundle);
+ HandwritingGesture gesture = null;
+ switch(mRichGestureMode) {
+ case OP_SELECT:
+ SelectGesture.Builder builder = new SelectGesture.Builder();
+ builder.setGranularity(mRichGestureGranularity)
+ .setSelectionArea(new RectF(mRichGestureStartPoint.x,
+ mRichGestureStartPoint.y, event.getX(), event.getY()))
+ .setFallbackText("fallback text");
+ gesture = builder.build();
+ break;
+ case OP_DELETE:
+ DeleteGesture.Builder builder1 = new DeleteGesture.Builder();
+ builder1.setGranularity(mRichGestureGranularity)
+ .setDeletionArea(new RectF(mRichGestureStartPoint.x,
+ mRichGestureStartPoint.y, event.getX(), event.getY()))
+ .setFallbackText("fallback text");
+ gesture = builder1.build();
+ break;
+ case OP_INSERT:
+ InsertGesture.Builder builder2 = new InsertGesture.Builder();
+ builder2.setInsertionPoint(
+ new PointF(mRichGestureStartPoint.x, mRichGestureStartPoint.y))
+ .setTextToInsert(" ")
+ .setFallbackText("fallback text");
+ gesture = builder2.build();
+
+ }
+ if (gesture == null) {
+ // This shouldn't happen
+ Log.e(TAG, "Unrecognized gesture mode: " + mRichGestureMode);
+ return;
+ }
+ InputConnection ic = getCurrentInputConnection();
+ if (getCurrentInputStarted() && ic != null) {
+ ic.performHandwritingGesture(gesture, null, null);
+ } else {
+ // This shouldn't happen
+ Log.e(TAG, "No active InputConnection");
+ }
Log.d(TAG, "Sending RichGesture " + mRichGestureMode + " (Screen) Left: "
+ mRichGestureStartPoint.x + ", Top: " + mRichGestureStartPoint.y
@@ -123,8 +159,15 @@
view.addView(inner, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, height));
- view.addView(getRichGestureActionsSpinner());
- inner.setBackgroundColor(getColor(R.color.abc_tint_spinner));
+ LinearLayout layout = new LinearLayout(this);
+ layout.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(getRichGestureActionsSpinner());
+ layout.addView(getRichGestureGranularitySpinner());
+
+ view.addView(layout);
+ inner.setBackgroundColor(getColor(R.color.holo_green_light));
return view;
}
@@ -133,14 +176,12 @@
if (mRichGestureModeSpinner != null) {
return mRichGestureModeSpinner;
}
- //get the spinner from the xml.
mRichGestureModeSpinner = new Spinner(this);
mRichGestureModeSpinner.setPadding(100, 0, 100, 0);
mRichGestureModeSpinner.setTooltipText("Handwriting IME mode");
String[] items =
new String[] { "Handwriting IME - Rich gesture disabled", "Rich gesture SELECT",
- "Rich gesture DELETE", "Rich gesture DELETE SPACE",
- "Rich gesture INSERT" };
+ "Rich gesture DELETE", "Rich gesture INSERT" };
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_dropdown_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@@ -149,17 +190,52 @@
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mRichGestureMode = position;
+ mRichGestureGranularitySpinner.setEnabled(
+ mRichGestureMode != OP_INSERT && mRichGestureMode != OP_NONE);
Log.d(TAG, "Setting RichGesture Mode " + mRichGestureMode);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
mRichGestureMode = OP_NONE;
+ mRichGestureGranularitySpinner.setEnabled(false);
}
});
+ mRichGestureModeSpinner.setSelection(0); // default disabled
return mRichGestureModeSpinner;
}
+ private View getRichGestureGranularitySpinner() {
+ if (mRichGestureGranularitySpinner != null) {
+ return mRichGestureGranularitySpinner;
+ }
+ mRichGestureGranularitySpinner = new Spinner(this);
+ mRichGestureGranularitySpinner.setPadding(100, 0, 100, 0);
+ mRichGestureGranularitySpinner.setTooltipText(" Granularity");
+ String[] items =
+ new String[] { "Granularity - UNDEFINED",
+ "Granularity - WORD", "Granularity - CHARACTER"};
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_spinner_dropdown_item, items);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mRichGestureGranularitySpinner.setAdapter(adapter);
+ mRichGestureGranularitySpinner.setOnItemSelectedListener(
+ new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mRichGestureGranularity = position;
+ Log.d(TAG, "Setting RichGesture Granularity " + mRichGestureGranularity);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ mRichGestureGranularity = 0;
+ }
+ });
+ mRichGestureGranularitySpinner.setSelection(1);
+ return mRichGestureGranularitySpinner;
+ }
+
public void onPrepareStylusHandwriting() {
Log.d(TAG, "onPrepareStylusHandwriting ");
if (mInk == null) {
@@ -190,15 +266,6 @@
return false;
}
- boolean performPrivateCommand(String action, Bundle bundle) {
- if (!getCurrentInputStarted()) {
- Log.e(TAG, "Input hasnt started, can't performPrivateCommand");
- return false;
- }
-
- return getCurrentInputConnection().performPrivateCommand(action, bundle);
- }
-
private boolean areRichGesturesEnabled() {
return mRichGestureMode != OP_NONE;
}
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
index c9e429b..94b1f86 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
@@ -30,7 +30,7 @@
import android.view.WindowMetrics;
class InkView extends View {
- private static final long FINISH_TIMEOUT = 600;
+ private static final long FINISH_TIMEOUT = 1500;
private final HandwritingIme.HandwritingFinisher mHwCanceller;
private final HandwritingIme.StylusConsumer mConsumer;
private final int mTopInset;
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 9f6ce4e..b7c4c5b 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -61,6 +61,7 @@
static_libs: ["RollbackTestLib", "frameworks-base-hostutils"],
test_suites: ["general-tests"],
test_config: "NetworkStagedRollbackTest.xml",
+ data: [":RollbackTest"],
}
java_test_host {
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 1709e15..ffde8c7 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -58,6 +58,7 @@
":apex.apexd_test",
":com.android.apex.apkrollback.test_v1",
":com.android.apex.apkrollback.test_v2",
+ ":StagedInstallInternalTestApp",
":StagedInstallTestApexV2",
":StagedInstallTestApexV2_WrongSha",
":TestAppAv1",
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index db48984..661dd84 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -33,6 +33,8 @@
import android.widget.ImageView;
import android.widget.TextView;
+import java.util.Arrays;
+
public class MainInteractionSession extends VoiceInteractionSession
implements View.OnClickListener {
static final String TAG = "MainInteractionSession";
@@ -403,7 +405,7 @@
@Override
public void onRequestPickOption(PickOptionRequest request) {
Log.i(TAG, "onPickOption: prompt=" + request.getVoicePrompt() + " options="
- + request.getOptions() + " extras=" + request.getExtras());
+ + Arrays.toString(request.getOptions()) + " extras=" + request.getExtras());
mConfirmButton.setText("Pick Option");
mPendingRequest = request;
setPrompt(request.getVoicePrompt());
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java
index 733f602..8ae7186 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java
@@ -24,6 +24,8 @@
import android.widget.Button;
import android.widget.TextView;
+import java.util.Arrays;
+
public class StartVoiceInteractionActivity extends Activity implements View.OnClickListener {
static final String TAG = "LocalVoiceInteractionActivity";
@@ -187,7 +189,8 @@
}
@Override
public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
- Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections
+ Log.i(TAG, "Pick result: finished=" + finished
+ + " selections=" + Arrays.toString(selections)
+ " result=" + result);
StringBuilder sb = new StringBuilder();
if (finished) {
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index ada0e21..4fc3a15 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -28,6 +28,8 @@
import android.widget.Button;
import android.widget.TextView;
+import java.util.Arrays;
+
public class TestInteractionActivity extends Activity implements View.OnClickListener {
static final String TAG = "TestInteractionActivity";
@@ -240,7 +242,8 @@
}
@Override
public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
- Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections
+ Log.i(TAG, "Pick result: finished=" + finished
+ + " selections=" + Arrays.toString(selections)
+ " result=" + result);
StringBuilder sb = new StringBuilder();
if (finished) {
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index dfa2291..f9e52b4 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -33,6 +33,7 @@
#include "ValueVisitor.h"
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
#include "idmap2/Policies.h"
#include "text/Printer.h"
#include "util/Util.h"
@@ -515,7 +516,8 @@
}
void Visit(const xml::Text* text) override {
- printer_->Println(StringPrintf("T: '%s'", text->text.c_str()));
+ printer_->Println(
+ StringPrintf("T: '%s'", android::ResTable::normalizeForOutput(text->text.c_str()).c_str()));
}
private:
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 46a846b..4fb7ed1 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -369,9 +369,13 @@
bool sparse_encode = use_sparse_entries_;
- // Only sparse encode if the entries will be read on platforms O+.
- sparse_encode =
- sparse_encode && (context_->GetMinSdkVersion() >= SDK_O || config.sdkVersion >= SDK_O);
+ if (context_->GetMinSdkVersion() == 0 && config.sdkVersion == 0) {
+ // Sparse encode if sdk version is not set in context and config.
+ } else {
+ // Otherwise, only sparse encode if the entries will be read on platforms S_V2+.
+ sparse_encode = sparse_encode &&
+ (context_->GetMinSdkVersion() >= SDK_S_V2 || config.sdkVersion >= SDK_S_V2);
+ }
// Only sparse encode if the offsets are representable in 2 bytes.
sparse_encode =
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e48fca6..f551bf6 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -330,7 +330,7 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
.SetCompilationPackage("android")
.SetPackageId(0x01)
- .SetMinSdkVersion(SDK_O)
+ .SetMinSdkVersion(SDK_S_V2)
.Build();
const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
@@ -376,7 +376,7 @@
.SetMinSdkVersion(SDK_LOLLIPOP)
.Build();
- const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB-v26");
+ const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB-v32");
auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
TableFlattenerOptions options;
@@ -391,6 +391,46 @@
EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
}
+TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) {
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetCompilationPackage("android").SetPackageId(0x01).Build();
+
+ const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
+ auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
+
+ TableFlattenerOptions options;
+ options.use_sparse_entries = true;
+
+ std::string no_sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
+
+ std::string sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
+
+ EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
+
+ // Attempt to parse the sparse contents.
+
+ ResourceTable sparse_table;
+ BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"),
+ sparse_contents.data(), sparse_contents.size());
+ ASSERT_TRUE(parser.Parse());
+
+ auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0",
+ sparse_config);
+ ASSERT_THAT(value, NotNull());
+ EXPECT_EQ(0u, value->value.data);
+
+ ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
+ sparse_config),
+ IsNull());
+
+ value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
+ sparse_config);
+ ASSERT_THAT(value, NotNull());
+ EXPECT_EQ(4u, value->value.data);
+}
+
TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
.SetCompilationPackage("android")