diff options
510 files changed, 10402 insertions, 8210 deletions
diff --git a/Android.bp b/Android.bp index c0a70b99827e..2c550fdfb675 100644 --- a/Android.bp +++ b/Android.bp @@ -428,6 +428,15 @@ java_library { 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 1d92778a9117..9a4323a119f0 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -25,6 +25,6 @@ hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/c 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 3781b6dcf162..03e5468a342a 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 @@ public class BlobStorePerfTests { 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 0f3a0681f85e..53e5d99409e2 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 f4d0c053fb66..45d164c96cd8 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 android.view.SurfaceControl; 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 class SurfaceFlingerPerfTest { 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 a9b2a3118bc1..832a0cd1e917 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 @@ public class SurfaceFlingerTestActivity extends Activity { 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 f49cdbf403f0..ab0ac5aa8856 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 @@ public class JobInfo implements Parcelable { * 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 @@ public class JobInfo implements Parcelable { * <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 632ecb2c0381..dfdb29091ad9 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 @@ import java.util.List; * </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 448a808ff52f..3ce9e6dc7e08 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 @@ public class InternalResourceService extends SystemService { return; } synchronized (mLock) { + mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties()); loadInstalledPackageListLocked(); final boolean isFirstSetup = !mScribe.recordExists(); if (isFirstSetup) { @@ -803,10 +804,7 @@ public class InternalResourceService extends SystemService { 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 50a0b75b3276..a832dea20673 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 @@ license { java_binary { name: "abx", - wrapper: "abx", + wrapper: "abx.sh", srcs: ["**/*.java"], required: [ "abx2xml", @@ -28,10 +27,10 @@ java_binary { 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 index 0a9362d64398..0a9362d64398 100755 --- a/cmds/abx/abx +++ b/cmds/abx/abx.sh diff --git a/cmds/abx/abx2xml b/cmds/abx/abx2xml.sh index 0a9362d64398..0a9362d64398 100755 --- a/cmds/abx/abx2xml +++ b/cmds/abx/abx2xml.sh diff --git a/cmds/abx/xml2abx b/cmds/abx/xml2abx.sh index 0a9362d64398..0a9362d64398 100755 --- a/cmds/abx/xml2abx +++ b/cmds/abx/xml2abx.sh diff --git a/cmds/bmgr/Android.bp b/cmds/bmgr/Android.bp index 14beb5561dba..a85669a20e99 100644 --- a/cmds/bmgr/Android.bp +++ b/cmds/bmgr/Android.bp @@ -20,6 +20,6 @@ license { java_binary { name: "bmgr", - wrapper: "bmgr", + wrapper: "bmgr.sh", srcs: ["**/*.java"], } diff --git a/cmds/bmgr/bmgr b/cmds/bmgr/bmgr.sh index b068d10dcbcf..b068d10dcbcf 100755 --- a/cmds/bmgr/bmgr +++ b/cmds/bmgr/bmgr.sh diff --git a/cmds/bu/Android.bp b/cmds/bu/Android.bp index 5b4ec3197cdd..b61a7a6e6429 100644 --- a/cmds/bu/Android.bp +++ b/cmds/bu/Android.bp @@ -20,6 +20,6 @@ license { java_binary { name: "bu", - wrapper: "bu", + wrapper: "bu.sh", srcs: ["**/*.java"], } diff --git a/cmds/bu/bu b/cmds/bu/bu.sh index e50b53da4325..e50b53da4325 100755 --- a/cmds/bu/bu +++ b/cmds/bu/bu.sh diff --git a/cmds/content/Android.bp b/cmds/content/Android.bp index c70d01ec8f0b..0dd0f034e405 100644 --- a/cmds/content/Android.bp +++ b/cmds/content/Android.bp @@ -19,6 +19,6 @@ license { java_binary { name: "content", - wrapper: "content", + wrapper: "content.sh", srcs: ["**/*.java"], } diff --git a/cmds/content/content b/cmds/content/content.sh index 91f2dfb16ac9..91f2dfb16ac9 100755 --- a/cmds/content/content +++ b/cmds/content/content.sh diff --git a/cmds/device_config/Android.bp b/cmds/device_config/Android.bp index 69572d8690c4..83b2730c874f 100644 --- a/cmds/device_config/Android.bp +++ b/cmds/device_config/Android.bp @@ -14,5 +14,5 @@ package { 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 index a949bd528263..a949bd528263 100755 --- a/cmds/device_config/device_config +++ b/cmds/device_config/device_config.sh diff --git a/cmds/dpm/Android.bp b/cmds/dpm/Android.bp index 6819d0982d2c..29bee4958be6 100644 --- a/cmds/dpm/Android.bp +++ b/cmds/dpm/Android.bp @@ -20,5 +20,5 @@ license { sh_binary { name: "dpm", - src: "dpm", + src: "dpm.sh", } diff --git a/cmds/dpm/dpm b/cmds/dpm/dpm.sh index 784db5b352a9..784db5b352a9 100755 --- a/cmds/dpm/dpm +++ b/cmds/dpm/dpm.sh diff --git a/cmds/hid/Android.bp b/cmds/hid/Android.bp index 295c71c65368..a6e27698e36c 100644 --- a/cmds/hid/Android.bp +++ b/cmds/hid/Android.bp @@ -20,7 +20,7 @@ license { 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 index 43c76345c9e1..43c76345c9e1 100755 --- a/cmds/hid/hid +++ b/cmds/hid/hid.sh diff --git a/cmds/ime/Android.bp b/cmds/ime/Android.bp index 6dd3ba1ca7ef..5f54ffacbb00 100644 --- a/cmds/ime/Android.bp +++ b/cmds/ime/Android.bp @@ -20,5 +20,5 @@ license { sh_binary { name: "ime", - src: "ime", + src: "ime.sh", } diff --git a/cmds/ime/ime b/cmds/ime/ime.sh index 7d2f72f74010..7d2f72f74010 100755 --- a/cmds/ime/ime +++ b/cmds/ime/ime.sh diff --git a/cmds/input/Android.bp b/cmds/input/Android.bp index 2e30176971d7..8f44f3e34ef7 100644 --- a/cmds/input/Android.bp +++ b/cmds/input/Android.bp @@ -20,5 +20,5 @@ license { sh_binary { name: "input", - src: "input", + src: "input.sh", } diff --git a/cmds/input/input b/cmds/input/input.sh index d7d041431b49..d7d041431b49 100755 --- a/cmds/input/input +++ b/cmds/input/input.sh diff --git a/cmds/locksettings/Android.bp b/cmds/locksettings/Android.bp index 3869c8fcdabc..5ee582450361 100644 --- a/cmds/locksettings/Android.bp +++ b/cmds/locksettings/Android.bp @@ -23,6 +23,6 @@ package { java_binary { name: "locksettings", - wrapper: "locksettings", + wrapper: "locksettings.sh", srcs: ["**/*.java"], } diff --git a/cmds/locksettings/locksettings b/cmds/locksettings/locksettings.sh index 0ef4fa9f6771..0ef4fa9f6771 100755 --- a/cmds/locksettings/locksettings +++ b/cmds/locksettings/locksettings.sh diff --git a/cmds/pm/Android.bp b/cmds/pm/Android.bp index 847dbabdf4c0..0e61a9e35d5e 100644 --- a/cmds/pm/Android.bp +++ b/cmds/pm/Android.bp @@ -20,5 +20,5 @@ license { sh_binary { name: "pm", - src: "pm", + src: "pm.sh", } diff --git a/cmds/pm/pm b/cmds/pm/pm.sh index 4d1f94554a78..4d1f94554a78 100755 --- a/cmds/pm/pm +++ b/cmds/pm/pm.sh diff --git a/cmds/requestsync/Android.bp b/cmds/requestsync/Android.bp index 57e8dd355b26..8718f79c0d70 100644 --- a/cmds/requestsync/Android.bp +++ b/cmds/requestsync/Android.bp @@ -20,6 +20,6 @@ license { java_binary { name: "requestsync", - wrapper: "requestsync", + wrapper: "requestsync.sh", srcs: ["**/*.java"], } diff --git a/cmds/requestsync/requestsync b/cmds/requestsync/requestsync.sh index 2d5d0e41705a..2d5d0e41705a 100755 --- a/cmds/requestsync/requestsync +++ b/cmds/requestsync/requestsync.sh diff --git a/cmds/screencap/OWNERS b/cmds/screencap/OWNERS new file mode 100644 index 000000000000..89f117782844 --- /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 cc730062f9df..8180fd6cab61 100644 --- a/cmds/settings/Android.bp +++ b/cmds/settings/Android.bp @@ -14,5 +14,5 @@ package { sh_binary { name: "settings", - src: "settings", + src: "settings.sh", } diff --git a/cmds/settings/settings b/cmds/settings/settings.sh index d41ccc62811a..d41ccc62811a 100755 --- a/cmds/settings/settings +++ b/cmds/settings/settings.sh diff --git a/cmds/sm/Android.bp b/cmds/sm/Android.bp index ecfacaeecfd9..403022a2d2ae 100644 --- a/cmds/sm/Android.bp +++ b/cmds/sm/Android.bp @@ -20,6 +20,6 @@ license { java_binary { name: "sm", - wrapper: "sm", + wrapper: "sm.sh", srcs: ["**/*.java"], } diff --git a/cmds/sm/sm b/cmds/sm/sm.sh index 30eae00a990d..30eae00a990d 100755 --- a/cmds/sm/sm +++ b/cmds/sm/sm.sh diff --git a/cmds/svc/Android.bp b/cmds/svc/Android.bp index 41a3ebd2f514..a2460875f28e 100644 --- a/cmds/svc/Android.bp +++ b/cmds/svc/Android.bp @@ -20,6 +20,6 @@ license { java_binary { name: "svc", - wrapper: "svc", + wrapper: "svc.sh", srcs: ["**/*.java"], } diff --git a/cmds/svc/svc b/cmds/svc/svc.sh index a2c9de32b3d0..a2c9de32b3d0 100755 --- a/cmds/svc/svc +++ b/cmds/svc/svc.sh diff --git a/cmds/telecom/Android.bp b/cmds/telecom/Android.bp index 4da79c54477b..be027105ae98 100644 --- a/cmds/telecom/Android.bp +++ b/cmds/telecom/Android.bp @@ -20,6 +20,6 @@ license { java_binary { name: "telecom", - wrapper: "telecom", + wrapper: "telecom.sh", srcs: ["**/*.java"], } diff --git a/cmds/telecom/telecom b/cmds/telecom/telecom.sh index a19036bf5bd8..a19036bf5bd8 100755 --- a/cmds/telecom/telecom +++ b/cmds/telecom/telecom.sh diff --git a/cmds/uiautomator/cmds/uiautomator/Android.bp b/cmds/uiautomator/cmds/uiautomator/Android.bp index 56e2e70958fa..51323860725b 100644 --- a/cmds/uiautomator/cmds/uiautomator/Android.bp +++ b/cmds/uiautomator/cmds/uiautomator/Android.bp @@ -25,7 +25,7 @@ package { 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 index 889c2b524dfd..889c2b524dfd 100755 --- a/cmds/uiautomator/cmds/uiautomator/uiautomator +++ b/cmds/uiautomator/cmds/uiautomator/uiautomator.sh diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp index 260cfc781ebc..4b08d968ac4a 100644 --- a/cmds/uinput/Android.bp +++ b/cmds/uinput/Android.bp @@ -20,9 +20,10 @@ license { 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 index ab2770ee2043..ab2770ee2043 100755 --- a/cmds/uinput/uinput +++ b/cmds/uinput/uinput.sh diff --git a/cmds/vr/Android.bp b/cmds/vr/Android.bp index 893649155fd8..61795b4cc052 100644 --- a/cmds/vr/Android.bp +++ b/cmds/vr/Android.bp @@ -20,6 +20,6 @@ license { java_binary { name: "vr", - wrapper: "vr", + wrapper: "vr.sh", srcs: ["**/*.java"], } diff --git a/cmds/vr/vr b/cmds/vr/vr.sh index dbde02a4ac7f..dbde02a4ac7f 100755 --- a/cmds/vr/vr +++ b/cmds/vr/vr.sh diff --git a/cmds/wm/Android.bp b/cmds/wm/Android.bp index cf6b0193fe87..4d00f175211d 100644 --- a/cmds/wm/Android.bp +++ b/cmds/wm/Android.bp @@ -20,5 +20,5 @@ license { sh_binary { name: "wm", - src: "wm", + src: "wm.sh", } diff --git a/cmds/wm/wm b/cmds/wm/wm.sh index cb45be20c24a..cb45be20c24a 100755 --- a/cmds/wm/wm +++ b/cmds/wm/wm.sh diff --git a/core/api/current.txt b/core/api/current.txt index 2411a4e42f56..a26ca4fceae9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -32172,6 +32172,7 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os.strictmode { 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 @@ package android.view.accessibility { 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 @@ package android.view.accessibility { 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 ef8ae7095d65..a87f2e2bdaee 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -6134,6 +6134,11 @@ package android.media { 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 @@ package android.media { 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 53014a3c580e..368087c860b8 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -793,6 +793,7 @@ package android.content.pm { 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 @@ package android.os { 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.storage { } -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 b9ad595778e4..7215987b0d87 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -753,7 +753,7 @@ public final class NotificationChannel implements Parcelable { /** * 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 7014d6963538..a5a50d695793 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -43,6 +43,7 @@ import java.io.FileDescriptor; 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 @@ public final class VoiceInteractor { } 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 68665107dfde..c12e26b21f13 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 class DevicePolicyManager { */ 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 24b4f4b99ad0..63c9839a87e3 100644 --- a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java +++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java @@ -203,17 +203,14 @@ public final class PreferentialNetworkServiceConfig implements Parcelable { 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 000000000000..9133d7fe6634 --- /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 11e695ad7fad..af48fde6423a 100644 --- a/core/java/android/app/ambientcontext/AmbientContextEvent.java +++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java @@ -59,11 +59,21 @@ public final class AmbientContextEvent implements Parcelable { */ 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 @@ public final class AmbientContextEvent implements Parcelable { @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 @@ public final class AmbientContextEvent implements Parcelable { 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 @@ public final class AmbientContextEvent implements Parcelable { } @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 308c5edfd0f4..9cb1a204b312 100644 --- a/core/java/android/app/ambientcontext/AmbientContextManager.java +++ b/core/java/android/app/ambientcontext/AmbientContextManager.java @@ -282,7 +282,7 @@ public final class AmbientContextManager { 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 @@ public final class AmbientContextManager { } /** + * 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 0d9ecfdfe8f6..8f06e76db559 100644 --- a/core/java/android/app/ambientcontext/IAmbientContextManager.aidl +++ b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl @@ -18,6 +18,7 @@ package android.app.ambientcontext; import android.app.PendingIntent; import android.app.ambientcontext.AmbientContextEventRequest; +import android.app.ambientcontext.IAmbientContextObserver; import android.os.RemoteCallback; /** @@ -29,6 +30,11 @@ interface IAmbientContextManager { 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/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt b/core/java/android/app/ambientcontext/IAmbientContextObserver.aidl index 1fb4ca1ac5db..529e2fea7edc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt +++ b/core/java/android/app/ambientcontext/IAmbientContextObserver.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 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,18 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.people +package android.app.ambientcontext; -object EmptySubscription : Subscription { - override fun unsubscribe() {} +import android.app.ambientcontext.AmbientContextEvent; + +/** + * Callback interface of AmbientContextManager. + * + * @hide + */ +oneway interface IAmbientContextObserver { + void onEvents(in List<AmbientContextEvent> events); + void onRegistrationComplete(in int statusCode); } -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/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java index e6091f04a72a..2934cd27da19 100644 --- a/core/java/android/companion/BluetoothLeDeviceFilter.java +++ b/core/java/android/companion/BluetoothLeDeviceFilter.java @@ -204,9 +204,10 @@ public final class BluetoothLeDeviceFilter implements DeviceFilter<ScanResult> { @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 3f2fa2188d24..b0c6cbced4f9 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -495,14 +495,9 @@ public final class AttributionSource implements Parcelable { @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 896fe91696db..f593ed9103b1 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7163,12 +7163,18 @@ public class Intent implements Parcelable, Cloneable { */ 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 @@ public class Intent implements Parcelable, Cloneable { // 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 @@ public class Intent implements Parcelable, Cloneable { // 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 @@ public class Intent implements Parcelable, Cloneable { * @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 @@ public class Intent implements Parcelable, Cloneable { 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 @@ public class Intent implements Parcelable, Cloneable { } } - 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 885eb708d92c..ffd80eabf6ca 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 class RestrictionsManager { } 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 e92be7cb64e2..26c947b5f916 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1120,6 +1120,17 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { 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 f780f815fa89..f8c49744d834 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -1723,7 +1723,8 @@ public class LauncherApps { 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 class LauncherApps { 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 @@ public class LauncherApps { 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 class LauncherApps { 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 52774e354c90..1f83d7532f04 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -53,6 +53,7 @@ import com.android.internal.util.Preconditions; 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 @@ public final class ShortcutInfo implements Parcelable { 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 70b90e6fdb1a..e48a02a192d2 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 @@ public class ShortcutManager { 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 35ad5033e395..7bc551d7f49e 100644 --- a/core/java/android/content/res/GradientColor.java +++ b/core/java/android/content/res/GradientColor.java @@ -22,13 +22,6 @@ import android.annotation.NonNull; 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.AttributeSet; 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 @@ public class GradientColor extends ComplexColor { } 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 5bdd96677da5..1333f0da725f 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 55cab52fc4f7..2e8e763b0ee4 100644 --- a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl +++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl @@ -23,5 +23,8 @@ package android.hardware.biometrics; * @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 6300a12c3137..a13eadaefe5a 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -491,8 +491,12 @@ public final class ProgramSelector implements Parcelable { 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 8180caa1557b..43341166d720 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -505,7 +505,8 @@ public class RadioManager { 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 @@ public class RadioManager { 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 48b9b8869cae..3a157d30d4c4 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -107,6 +107,7 @@ import android.view.KeyCharacterMap; 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 @@ public class InputMethodService extends AbstractInputMethodService { * @hide */ @Override - public void updateEditorToolType(int toolType) { + public void updateEditorToolType(@ToolType int toolType) { onUpdateEditorToolType(toolType); } @@ -3079,10 +3080,13 @@ public class InputMethodService extends AbstractInputMethodService { * {@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 a19eb5668d55..8644d9103dcb 100644 --- a/core/java/android/metrics/LogMaker.java +++ b/core/java/android/metrics/LogMaker.java @@ -23,6 +23,7 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import java.util.Arrays; /** @@ -395,7 +396,7 @@ public class LogMaker { 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 43a6be5df202..fb8f84acc8f3 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -469,7 +469,15 @@ public class GraphicsEnvironment { * 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 @@ public class GraphicsEnvironment { 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 412a33a1c124..113a64048796 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -26,6 +26,9 @@ import android.annotation.TestApi; 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 final class StrictMode { 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 @@ public final class StrictMode { * <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 @@ public final class StrictMode { if (targetSdk >= Build.VERSION_CODES.O) { detectUnbufferedIo(); } + if (CompatChanges.isChangeEnabled(DETECT_EXPLICIT_GC)) { + detectExplicitGc(); + } return this; } @@ -591,26 +605,16 @@ public final class StrictMode { } /** - * 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 06930bb32090..392bf4ec3ece 100644 --- a/core/java/android/os/VibrationAttributes.java +++ b/core/java/android/os/VibrationAttributes.java @@ -145,7 +145,8 @@ public final class VibrationAttributes implements Parcelable { */ @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 @@ public final class VibrationAttributes implements Parcelable { * * @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 13b22d370030..a1ed2533f544 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -26,6 +26,7 @@ import android.os.RemoteException; 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 @@ public final class IncrementalStorage { 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 583ed1ab4505..c4ae82ddbc05 100644 --- a/core/java/android/os/strictmode/ExplicitGcViolation.java +++ b/core/java/android/os/strictmode/ExplicitGcViolation.java @@ -19,10 +19,7 @@ import android.annotation.TestApi; /** * 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 7189c886aa06..204353942b03 100644 --- a/core/java/android/service/autofill/OptionalValidators.java +++ b/core/java/android/service/autofill/OptionalValidators.java @@ -25,6 +25,8 @@ import android.util.Log; 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 @@ final class OptionalValidators extends InternalValidator { 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 619eba0a79ad..054582e01f71 100644 --- a/core/java/android/service/autofill/RequiredValidators.java +++ b/core/java/android/service/autofill/RequiredValidators.java @@ -25,6 +25,8 @@ import android.util.Log; 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 @@ final class RequiredValidators extends InternalValidator { 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 d08a67e68676..f0f16dbbb39e 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -75,6 +75,7 @@ import java.io.FileDescriptor; 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 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall 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 3bde32b1e5f2..3177c5c8509e 100644 --- a/core/java/android/speech/tts/FileSynthesisCallback.java +++ b/core/java/android/speech/tts/FileSynthesisCallback.java @@ -24,6 +24,7 @@ import java.io.IOException; 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 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { @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 000000000000..e3976a7eab35 --- /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 4efc8381a112..b5f7c545aa07 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -19,11 +19,13 @@ package android.text; 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 @@ public abstract class Layout { } /** - * 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 @@ public abstract class Layout { 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 @@ public abstract class Layout { 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 @@ public abstract class Layout { } /** + * 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 000000000000..00522e542eec --- /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 000000000000..11153192b286 --- /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 c0bc9912af3d..155f508f6242 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -23,6 +23,7 @@ import com.android.internal.util.ArrayUtils; import libcore.util.EmptyArray; +import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Map; @@ -231,7 +232,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { } 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { } 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 b5c75b9276b7..73114e26e417 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage; 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 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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 bca100a1d81e..20f01ae3ae0d 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -68,6 +68,12 @@ public class FeatureFlagUtils { 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 class FeatureFlagUtils { */ 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 @@ public class FeatureFlagUtils { 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 @@ public class FeatureFlagUtils { 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 9789b10a0a61..9a15cd5dbfff 100644 --- a/core/java/android/util/proto/ProtoInputStream.java +++ b/core/java/android/util/proto/ProtoInputStream.java @@ -21,6 +21,8 @@ import android.util.LongArray; 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 final class ProtoInputStream extends ProtoStream { 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 8464d2d808d6..58d991370f56 100644 --- a/core/java/android/util/proto/ProtoUtils.java +++ b/core/java/android/util/proto/ProtoUtils.java @@ -20,6 +20,7 @@ import android.util.AggStats; 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 @@ public class ProtoUtils { 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 @@ public class ProtoUtils { 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 380c1045aea8..9e25a3e18c0c 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -116,8 +116,10 @@ public final class FrameMetrics { * 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 57ba7e9e816f..f9bb880b1881 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -653,7 +653,7 @@ public class GestureDetector { break; case MotionEvent.ACTION_MOVE: - if (mInLongPress || mInContextClick) { + if ((mCurrentDownEvent == null) || mInLongPress || mInContextClick) { break; } @@ -736,6 +736,9 @@ public class GestureDetector { 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 0bed342a8b27..ea0012543ba9 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1509,6 +1509,13 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ 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 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @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 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @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 @@ public final class MotionEvent extends InputEvent implements Parcelable { * * @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 e0f02d6e567a..2a45fa563abf 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -1943,7 +1943,7 @@ public final class SurfaceControl implements Parcelable { @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 e8179cf59c1a..9620b0951d56 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -9575,7 +9575,7 @@ public final class ViewRootImpl implements ViewParent, /** * 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 6ae59bf0c8fc..35ec40866d65 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -84,7 +84,7 @@ import java.util.Objects; * <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 @@ public class AccessibilityNodeInfo implements Parcelable { * @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 @@ public class AccessibilityNodeInfo implements Parcelable { * @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 @@ public class AccessibilityNodeInfo implements Parcelable { * @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 @@ public class AccessibilityNodeInfo implements Parcelable { * @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 @@ public class AccessibilityNodeInfo implements Parcelable { * {@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. + * 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> - * 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. + * 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> - * 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. + * 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 5515548cdb04..36b033485575 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -42,6 +42,7 @@ import android.text.TextUtils; 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 @@ public class EditorInfo implements InputType, Parcelable { * 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 @@ public class EditorInfo implements InputType, Parcelable { * @see InputMethodService#onUpdateEditorToolType(int) * @return toolType {@link MotionEvent#getToolType(int)}. */ - public int getInitialToolType() { + public @ToolType int getInitialToolType() { return mInitialToolType; } @@ -1034,7 +1034,7 @@ public class EditorInfo implements InputType, Parcelable { * @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 6825f034f5b3..f93b8b7c1b5f 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -226,8 +226,6 @@ public class Editor { 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 class Editor { 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 @@ public class Editor { } 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 @@ public class Editor { ? getSelectionController() : getInsertionController(); if (cursorController != null && !cursorController.isActive() && !cursorController.isCursorBeingModified() - && showUIForTouchScreen()) { + && mTextView.showUIForTouchScreen()) { cursorController.show(); } } @@ -2521,7 +2517,7 @@ public class Editor { return false; } - if (!showUIForTouchScreen()) { + if (!mTextView.showUIForTouchScreen()) { return false; } @@ -2677,7 +2673,7 @@ public class Editor { mTextView.postDelayed(mShowSuggestionRunnable, ViewConfiguration.getDoubleTapTimeout()); } else if (hasInsertionController()) { - if (shouldInsertCursor && showUIForTouchScreen()) { + if (shouldInsertCursor && mTextView.showUIForTouchScreen()) { getInsertionController().show(); } else { getInsertionController().hide(); @@ -5408,7 +5404,7 @@ public class Editor { 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 @@ public class Editor { } } - /** - * 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 4ccd77b2713e..be6b08fbddae 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -301,7 +301,7 @@ public final class SelectionActionModeHelper { 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 6a572c530a1e..f53dd0c53ef5 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -72,6 +72,7 @@ import android.graphics.Insets; 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.BoringLayout; 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.TextPaint; 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.BaseInputConnection; 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** @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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * 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 be7501fea7d0..9211926c51e2 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; 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.util.proto.ProtoOutputStream; 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 @@ public final class EditableInputConnection extends BaseInputConnection } @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 33cb56f4ce69..b5fc05bc9938 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -227,6 +227,8 @@ public final class InputMethodDebug { 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 b11ea2961c17..9ef7ce38fc09 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -19,6 +19,8 @@ package com.android.internal.widget; 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 @@ public class LocalImageResolver { } 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 @@ public class LocalImageResolver { } @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 @@ public class LocalImageResolver { } 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 9f3f335f3173..03e7fd1c7403 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.NonNull; 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 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @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 7f50204fb842..14699e7097f4 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -95,3 +95,7 @@ per-file android_os_VintfRuntimeInfo* = file:platform/system/libvintf:/OWNERS # 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 be82879c8411..acf4da6a5d07 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 @@ sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char 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 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr 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 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr 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 @@ public: } 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 @@ static install_status_t 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 @@ iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi, 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 @@ static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supported 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 @@ static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supported } } - for (int i = 0; i < numAbis; ++i) { - delete supportedAbis[i]; - } - return status; } @@ -521,19 +512,19 @@ static jint 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 b88315e2dbee..3342c79b7d3f 100644 --- a/core/proto/android/app/notificationmanager.proto +++ b/core/proto/android/app/notificationmanager.proto @@ -45,6 +45,8 @@ message PolicyProto { 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 93ce7832824b..7e17840445ab 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -130,6 +130,10 @@ android_app { // 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 6a52ec91fa32..e7cae76c60fc 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 e5b1cf9be748..8488b6865440 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 c5d6f7fad3e8..21a22057d7c8 100644 --- a/core/tests/coretests/src/android/provider/FontsContractTest.java +++ b/core/tests/coretests/src/android/provider/FontsContractTest.java @@ -42,6 +42,7 @@ import androidx.test.filters.SmallTest; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -282,9 +283,10 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { 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 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { 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 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { 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 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { // {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 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { 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 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { 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 000000000000..32fdb5e850bd --- /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 0cee526651a6..271a20b71106 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 @@ public class LocalImageResolverTest { // 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 f030d80a3533..e0e13f59b706 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 582488ff8de3..ca3c84729388 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -2796,7 +2796,8 @@ public abstract class ColorSpace { 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 c7062e093135..000000000000 --- 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 02af9160301c..4102732fd80e 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 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * 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 2b069d72e46f..2ef8e4c64855 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.WindowInsets; 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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * 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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { 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 77e26c07f304..45645b2018c3 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 @@ class TaskContainer { } 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 11c0db320646..344ffc759c46 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 @@ class TaskFragmentContainer { 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 da724d9d9311..25f0e25eec75 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 com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 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 @@ public class SplitPresenterTest { 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 6cbecff81be5..1bc81ee3dcc7 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 @@ public class TaskFragmentContainerTest { } @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_light_bulb.xml index 6fcd1de892a3..ddfb5c27e701 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml +++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_light_bulb.xml @@ -15,15 +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_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: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" /> + 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 cbfcfd06e3b7..22a8f39ca687 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 469eb1e14849..000000000000 --- 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 dcb8aed05c9c..15e65f716b20 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 cd1d99ae58b0..c65f24d84e37 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 95923763d889..3a44eb9089dd 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 1dac9caba01e..d2dd8d6b56a9 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 679bfb9d5187..b48a508fddb8 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 dcbb272feab8..d63c25d07485 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 @@ public class BubbleController implements ConfigurationChangeListener { 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 @@ public class BubbleController implements ConfigurationChangeListener { // 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 0e97e9e74114..453b34eb445c 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 @@ public interface Bubbles { 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 b7959eb629c1..4c85d2090ec5 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 final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange resetDividerPosition(); mDimNonImeSide = resources.getBoolean(R.bool.config_dimNonImeAttachedSide); + + updateInvisibleRect(); } private int getDividerInsets(Resources resources, Display display) { @@ -239,6 +247,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange : (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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mRotation = rotation; mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); initDividerPosition(mTempRect); + updateInvisibleRect(); return true; } @@ -405,6 +428,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 0d75bc451b72..f1465f421c5b 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 @@ public class FullscreenTaskListener<T extends AutoCloseable> 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 @@ public class FullscreenTaskListener<T extends AutoCloseable> 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 7e83d2fa0a0b..c08aa5a6f208 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_HOME; 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // 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 67d7aca8db8c..298bf68c41a7 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 @@ abstract class BaseBubbleScreen( @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 293eb7cd5581..47557bcead2f 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 @@ package com.android.wm.shell.flicker.bubble 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 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr } } - @Presubmit + @FlakyTest(bugId = 242088970) @Test fun testAppIsVisibleAtEnd() { testSpec.assertLayersEnd { @@ -125,7 +124,7 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @FlakyTest(bugId = 242088970) @Test override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() @@ -135,4 +134,28 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr @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 6695c17ed514..1b8a44bbe78e 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 @@ class LaunchBubbleHelper(instrumentation: Instrumentation) : BaseAppHelper( ) { 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 e7f9d9a9d73d..87a863d3d91c 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 @@ class SplitScreenHelper( ) : 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 d194472b9000..6fcd17ae5f8a 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 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec 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 507562b00f4f..a99439e35291 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 @@ class EnterPipToOtherOrientationTest( 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 b5a3c78b67a7..39a7017a16d0 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 @@ class ExitPipViaExpandButtonClickTest( @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 1d266140f2e6..421a6fc38e4c 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 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit @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 b71a9d85d8cc..3bffef0ca793 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 @@ class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTran @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 31a39c190dd6..75d25e6ef67e 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 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti @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 fd661cfc3ee1..825aca3aaa16 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 @@ class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition 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 b3f0fb91ab73..d3e2ce1571b4 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 @@ open class MovePipDownShelfHeightChangeTest( @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 8bd5c548f6bd..3d64bbeb720b 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 @@ class MovePipUpShelfHeightChangeTest( @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 454927e57aa2..be39faeff321 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 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS @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 09248a167308..4dc98588217a 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 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS @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 6d64cb9e0dee..d5de22fb49c0 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 @@ open class SetRequestedOrientationWhilePinnedTest( @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 d23881475ad6..6cbb685850fa 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 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS @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 ba40c2740bb1..581826ef889f 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 @@ class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreen @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 6828589656d6..5c051e859d59 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 @@ class DismissSplitScreenByGoHome( @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 9ac7c230096b..9ca9ab01fd7b 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 @@ class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(tes @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 8401c1a910b8..8e2769fb75bd 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 @@ class EnterSplitScreenByDragFromAllApps( @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 168afda119a9..531d376e3588 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 @@ class EnterSplitScreenByDragFromNotification( @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 c1fce5f40b57..ea43c7ea0ddf 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 @@ class EnterSplitScreenByDragFromTaskbar( @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 8cb5d7c24ced..7ce23c5f9a4e 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 @@ class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreen @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 153056188d24..525e09a19d2f 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 @@ class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreen @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 20544bd2fc2f..c030603a6b55 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 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr @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 5a8604f2dccc..b8565f3e89b2 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 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas @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 adea66a49c46..20d7f2cf57e8 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 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB @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 95725bbfd855..695550dd8fa5 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 @@ public class SplitLayoutTests extends ShellTestCase { } 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 88f73d6cf251..b11e542472da 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -93,6 +93,10 @@ cc_defaults { 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 @@ cc_defaults { 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 7118afad975a..e97f077b347a 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -35,8 +35,8 @@ aidl_interface { "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 @@ aidl_interface { }, }, 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 c70887672f9e..40f6dc541f86 100644 --- a/media/java/android/media/AudioDeviceVolumeManager.java +++ b/media/java/android/media/AudioDeviceVolumeManager.java @@ -21,6 +21,8 @@ import android.annotation.IntDef; 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 @@ import java.util.concurrent.Executor; * @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 @@ public class AudioDeviceVolumeManager { 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 @@ public class AudioDeviceVolumeManager { @VolumeAdjustmentMode int mode); } + /** @hide */ static class ListenerInfo { final @NonNull OnAudioDeviceVolumeChangedListener mListener; final @NonNull Executor mExecutor; @@ -127,6 +145,7 @@ public class AudioDeviceVolumeManager { @GuardedBy("mDeviceVolumeListenerLock") private DeviceVolumeDispatcherStub mDeviceVolumeDispatcherStub; + /** @hide */ final class DeviceVolumeDispatcherStub extends IAudioDeviceVolumeDispatcher.Stub { /** * Register / unregister the stub @@ -305,6 +324,7 @@ public class AudioDeviceVolumeManager { * @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 @@ public class AudioDeviceVolumeManager { } /** + * @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 650f36059495..72190e370129 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -65,8 +65,6 @@ public class AudioSystem 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 @@ public class AudioSystem 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 @@ public class AudioSystem 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 ee2e448bfdac..63173207ec17 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -5092,9 +5092,12 @@ public class MediaPlayer extends PlayerBase @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 class MediaPlayer extends PlayerBase 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 @@ public class MediaPlayer extends PlayerBase 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 class MediaPlayer extends PlayerBase 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 @@ public class MediaPlayer extends PlayerBase // 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 @@ public class MediaPlayer extends PlayerBase 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 @@ public class MediaPlayer extends PlayerBase 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 @@ public class MediaPlayer extends PlayerBase 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 c61b0e57db1a..6b4f604025e2 100644 --- a/media/java/android/media/VolumeInfo.java +++ b/media/java/android/media/VolumeInfo.java @@ -18,6 +18,7 @@ package android.media; 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 @@ import java.util.Objects; /** * @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 31d596765bcc..f957498237a8 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.annotations.GuardedBy; import com.android.internal.util.Preconditions; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -182,7 +183,7 @@ public final class AudioProductStrategy implements Parcelable { AudioProductStrategy thatStrategy = (AudioProductStrategy) o; return mName == thatStrategy.mName && mId == thatStrategy.mId - && mAudioAttributesGroups.equals(thatStrategy.mAudioAttributesGroups); + && Arrays.equals(mAudioAttributesGroups, thatStrategy.mAudioAttributesGroups); } /** @@ -415,7 +416,7 @@ public final class AudioProductStrategy implements Parcelable { 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 79be92214455..d58111d1e9a7 100644 --- a/media/java/android/media/audiopolicy/AudioVolumeGroup.java +++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java @@ -114,7 +114,7 @@ public final class AudioVolumeGroup implements Parcelable { 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 e71ee204f564..51a2c9d9b50e 100644 --- a/media/java/android/media/metrics/PlaybackMetrics.java +++ b/media/java/android/media/metrics/PlaybackMetrics.java @@ -402,9 +402,10 @@ public final class PlaybackMetrics implements Parcelable { @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 31fb8d03c4a0..9bf126b7a875 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -35,7 +35,7 @@ interface ISession { 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 1bd12afdc026..9e265d8339dd 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -286,7 +286,7 @@ public final class MediaSession { @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 41914b887b2b..ebf1a75a58a4 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.media.Image; 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 @@ public class CameraErrorCollector extends ErrorCollector { 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 @@ public class CameraErrorCollector extends ErrorCollector { 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 @@ public class CameraErrorCollector extends ErrorCollector { 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 class CameraErrorCollector extends ErrorCollector { 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 class CameraErrorCollector extends ErrorCollector { 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 class CameraErrorCollector extends ErrorCollector { */ 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 7fe2735c9ad6..cd6ed2391608 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -381,7 +381,7 @@ int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) { 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 bff739636b3e..e645afa1df5a 100644 --- a/opengl/java/android/opengl/GLLogWrapper.java +++ b/opengl/java/android/opengl/GLLogWrapper.java @@ -2812,7 +2812,7 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 @@ class GLLogWrapper extends GLWrapperBase { 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 5689e34d0886..a984938e3a9e 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 d9f6a2720ab6..229960c68cd7 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 d0d46f715846..1f922b97bfc4 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 14e74312dd2c..16a87e589b8e 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 2000d9675ca4..428f2dc2eb35 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 d38013679ad4..5dc2aa0d61cb 100644 --- a/packages/SettingsLib/Spa/build.gradle +++ b/packages/SettingsLib/Spa/build.gradle @@ -22,7 +22,7 @@ buildscript { } } 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 e300624bcc95..3233d4d75631 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.framework.common.SettingsPageProviderReposito 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 a85ee2a2a309..fdcb1c2a5853 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.framework.theme.SettingsTheme 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 @@ object HomePageProvider : SettingsPageProvider { @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 314635176889..130cbd9f7b7b 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 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.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 000000000000..d3ca1d3f347e --- /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 index 90dacdbe5087..36c619f79f5a 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 index 6d5d4483ab5f..8be6a895f3a4 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 000000000000..7efa85b4ad78 --- /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 6a7b17ab1b27..e8d1ea2c7680 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.ComponentActivity 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 @@ open class BrowseActivity( @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 @@ open class BrowseActivity( } } } + + if (!destination.isNullOrEmpty()) { + LaunchedEffect(Unit) { + navController.navigate(destination) { + popUpTo(navController.graph.findStartDestination().id) { + inclusive = true + } + } + } + } } } @@ -68,6 +79,6 @@ open class BrowseActivity( 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 c68d5de0d7c7..32ef0bb3d19b 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 @@ fun navigator(route: String): () -> Unit { 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 27fdc916a434..bc316f71cf23 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 @@ data class SettingsColorScheme( 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 @@ private fun dynamicLightColorScheme(context: Context): SettingsColorScheme { 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 @@ private fun dynamicDarkColorScheme(context: Context): SettingsColorScheme { 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 index 0454ac37cfb7..4f77a89577ea 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 c9602543b364..b8e43609708d 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 androidx.compose.ui.res.stringResource 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 ee453f246623..8b530b0d7b9b 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 @@ fun SettingsScaffold( 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 000000000000..429b81a04659 --- /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 037d8c4e78b1..1ce49fa520b9 100644 --- a/packages/SettingsLib/Spa/tests/Android.bp +++ b/packages/SettingsLib/Spa/tests/Android.bp @@ -31,6 +31,7 @@ android_test { "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 be5a5ec40c4f..5f93a9f4e4b5 100644 --- a/packages/SettingsLib/Spa/tests/build.gradle +++ b/packages/SettingsLib/Spa/tests/build.gradle @@ -63,5 +63,6 @@ dependencies { 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 000000000000..1d95e3383b51 --- /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 000000000000..6c56d63c18c7 --- /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 dac79a0fbb16..eb9ce5e77c93 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.NavType 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 PACKAGE_NAME = "packageName" 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 @@ internal class TogglePermissionAppInfoPageProvider( @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 index e1354bdd9871..3cc5854b95e4 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 @@ interface TogglePermissionAppListModel<T : AppRecord> { 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 @@ interface TogglePermissionAppListModelFactory { } ) } + + /** + * 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 6d00d56e6952..f2eb96277b9b 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 NAME = "TogglePermissionAppList" 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 @@ internal class TogglePermissionAppListPageProvider( @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 @@ internal class TogglePermissionAppListPageProvider( } 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 19409865284c..b64dcca63ac9 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 @@ import java.util.List; 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 @@ public class A2dpProfile implements LocalBluetoothProfile { 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 62c83cf04a4f..51812f043908 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 @@ public class BluetoothEventManager { 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 32d5b787489a..3ca94dba027e 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 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> * 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 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> 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 cd3242a9f7c2..26a208026411 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 class CachedBluetoothDeviceManager { } 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 fc70ba40fb47..9b38238ca8b8 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 @@ public class CsipDeviceManager { // 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 @@ public class CsipDeviceManager { 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 bef1d9cbcde1..62552f914459 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.verify; 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 @@ public class CachedBluetoothDeviceManagerTest { */ @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 @@ public class CachedBluetoothDeviceManagerTest { // 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 @@ public class CachedBluetoothDeviceManagerTest { @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 @@ public class CachedBluetoothDeviceManagerTest { // 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 76c066cd029e..79e99387b2fa 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 @@ public class CachedBluetoothDeviceTest { 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 78dea891bc12..7649de649cf8 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 000000000000..3ca8dfe4fd38 --- /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 000000000000..50f3ffcaf968 --- /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 d88680669fe0..ae8e38e2634b 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 ec004296ff9d..000000000000 --- 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 000000000000..99a1b5cc19dd --- /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 000000000000..d1303ed88964 --- /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 e34d422b6dbc..a19145ddba6b 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 dca5ea836fa4..f88f46f2a3c8 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 bfdb17041ad7..c7b2ff34c55f 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 f7acf06350c6..6d1bcbbb8c99 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 e1957c09fef5..93175e19a287 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -418,6 +418,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard 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 6c452bd97ff6..d718a240bbfd 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 @@ class KeyguardUpdateMonitorLogger @Inject constructor( 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 f54bf026a686..000000000000 --- 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 000000000000..109be40ce10f --- /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 index 88e227b8ae35..7a2de7b6a78d 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 614a87fc758c..0cb4da429c46 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.PrivacyDotViewController; 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 @@ public class Dependency { @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 @@ public class Dependency { 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 4c400a81024f..2e13903814a5 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.settingslib.Utils; 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab decorProviders.addAll(mFaceScanningFactory.getProviders()); if (!hasHwLayer) { decorProviders.addAll(mRoundedCornerFactory.getProviders()); + decorProviders.addAll(mCutoutFactory.getProviders()); } return decorProviders; } @@ -379,6 +388,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio( getPhysicalPixelDisplaySizeRatio()); mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); + mCutoutFactory = getCutoutFactory(); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); updateHwLayerRoundedCornerDrawable(); setupDecorations(); @@ -483,18 +493,13 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab updateConfiguration(); } + @VisibleForTesting @Nullable - private View getOverlayView(@IdRes int id) { + View getOverlayView(@IdRes int id) { if (mOverlays == null) { return null; } @@ -565,18 +571,18 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } - @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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private boolean shouldDrawCutout() { - return shouldDrawCutout(mContext); + return mCutoutFactory.getHasProviders(); } static boolean shouldDrawCutout(Context context) { @@ -1284,7 +1218,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab 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 class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } public void setColor(int color) { + if (color == mColor) { + return; + } mColor = color; paint.setColor(mColor); invalidate(); @@ -1299,6 +1235,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @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 84e1c3d4c8f0..86837366e0b7 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 @@ public class AuthContainerView extends LinearLayout 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 class AuthContainerView extends LinearLayout 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 class AuthContainerView extends LinearLayout 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 @@ public class AuthContainerView extends LinearLayout 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 @@ public class AuthContainerView extends LinearLayout 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 282f25104c44..6b3f13452326 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 @@ package com.android.systemui.biometrics; 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 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @Background DelayableExecutor bgExecutor) { super(context); mExecution = execution; - mWakefulnessLifecycle = wakefulnessLifecycle; mUserManager = userManager; mLockPatternUtils = lockPatternUtils; mHandler = handler; @@ -625,11 +627,24 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba 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 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @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 51f39b358659..cd0fc3737594 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 @@ public interface AuthDialog extends Dumpable { 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 4fe2dd810a08..e2ef2477c836 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.keyguard.KeyguardUpdateMonitor 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 @@ class CameraGestureHelper @Inject constructor( ) } 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 da675de2b9ec..dec3d6b679c2 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 @@ class WiredChargingRippleController @Inject constructor( } 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 08393386c27f..c0cc6b41aafd 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.FrameLayout; 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 @@ final class WirelessChargingLayout extends FrameLayout { 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 8ba6f1c4a411..d60a22204b3d 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.people.widget.PeopleSpaceWidgetProvider; 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 @@ public abstract class DefaultBroadcastReceiverBinder { */ @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 6db3e82a77b0..8bb27a7bc217 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 @@ abstract class SystemUICoreStartableModule { @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 1b060e209579..a0ecd228edb4 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.QsFrameTranslateModule; 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 @@ public abstract class SystemUIModule { 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 @@ public abstract class SystemUIModule { 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 000000000000..cbed21cf65d6 --- /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 000000000000..991b54e8035e --- /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 de6d7278971c..0681f50726a8 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 class DecorProvider { 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 @@ abstract class DecorProvider { @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 @@ abstract class BoundDecorProvider : DecorProvider() { 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 dfb0b5aad912..45b8a08a9b45 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 @@ class OverlayWindow(private val context: Context) { 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 d5db63dc9093..75a97de10e7e 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 @@ import java.util.Set; 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 1c72e49eb06a..2503d3c3edad 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 @@ public class DreamHomeControlsComplication implements Complication { * 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 000000000000..21a51d1096d5 --- /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 567bdbc01170..a981f255a873 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 @@ public class SmartSpaceComplication implements Complication { 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 000000000000..ed05daf35ed9 --- /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 eb07238ce752..759d6ec3bf21 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 @@ import dagger.Provides; }, 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 @@ public interface RegisteredComplicationsModule { } /** + * 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 45237a358700..c4553c4cb299 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 @@ public class Flags { // 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 9e2b7c748631..a3dc77993d30 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.LocalBluetoothAdapter; 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.List; import java.util.Set; import javax.inject.Inject; +import javax.inject.Provider; /** */ @SysUISingleton @@ -106,6 +106,8 @@ public class KeyboardUI extends CoreStartable implements InputManager.OnTabletMo protected volatile Context mContext; + private final Provider<LocalBluetoothManager> mBluetoothManagerProvider; + private boolean mEnabled; private String mKeyboardName; private CachedBluetoothDeviceManager mCachedDeviceManager; @@ -122,8 +124,9 @@ public class KeyboardUI extends CoreStartable implements InputManager.OnTabletMo 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 @@ public class KeyboardUI extends CoreStartable implements InputManager.OnTabletMo 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 70e0d5fa9ba7..2c60d5d67dac 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.provider.Settings; 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.KeyguardUpdateMonitor; 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 class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @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 class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, if (targetUserId != ActivityManager.getCurrentUser()) { return; } - mLogger.logKeyguardDone(); + if (DEBUG) Log.d(TAG, "keyguardDone"); tryKeyguardDone(); } @@ -732,7 +727,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @Override public void onLaunchAnimationCancelled() { - mLogger.logOccludeLaunchAnimationCancelled(mOccluded); + Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: " + + mOccluded); } @Override @@ -857,7 +853,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */); - mLogger.logUnoccludeAnimationCancelled(mOccluded); + Log.d(TAG, "Unocclude animation cancelled. Occluded state is now: " + + mOccluded); } @Override @@ -916,11 +914,12 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private ScreenOnCoordinator mScreenOnCoordinator; private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator; - private KeyguardViewMediatorLogger mLogger; /** * Injected constructor. See {@link KeyguardModule}. @@ -1037,8 +1035,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mDreamOverlayStateController = dreamOverlayStateController; mActivityLaunchAnimator = activityLaunchAnimator; - mLogger = logger; mPowerButtonY = context.getResources().getDimensionPixelSize( R.dimen.physical_power_button_center_screen_location_y); @@ -1145,21 +1141,21 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private void handleSystemReady() { synchronized (this) { - mLogger.logOnSystemReady(); + if (DEBUG) Log.d(TAG, "onSystemReady"); mSystemReady = true; doKeyguardLocked(null); mUpdateMonitor.registerCallback(mUpdateCallback); @@ -1206,7 +1202,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, * {@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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 class KeyguardViewMediator extends CoreStartable implements Dumpable, */ 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // - 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mAnimatingScreenOff = false; cancelDoKeyguardLaterLocked(); cancelDoKeyguardForChildProfilesLocked(); - mLogger.logOnStartedWakingUp(mDelayedShowingSequence); + if (DEBUG) Log.d(TAG, "onStartedWakingUp, seq = " + mDelayedShowingSequence); notifyStartedWakingUp(); } mUpdateMonitor.dispatchStartedWakingUp(); @@ -1532,35 +1542,37 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, */ 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Thread.currentThread().interrupt(); } } - mLogger.logDoneWaitingUntilKeyguardVisible(); + if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible"); } } } @@ -1592,31 +1604,31 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, try { callback.onKeyguardExitResult(true); } catch (RemoteException e) { - mLogger.logFailedToCallOnKeyguardExitResultFalse(e); + Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); } } else { @@ -1637,7 +1649,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, try { callback.onKeyguardExitResult(false); } catch (RemoteException e) { - mLogger.logFailedToCallOnKeyguardExitResultFalse(e); + Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); } } } @@ -1655,8 +1667,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, * 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, */ 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, || ((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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, * @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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, */ 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, */ 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 class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, }; 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, }; private final Runnable mHideAnimationFinishedRunnable = () -> { - mLogger.logHideAnimationFinishedRunnable(); + Log.e(TAG, "mHideAnimationFinishedRunnable#run"); mHideAnimationRunning = false; tryKeyguardDone(); }; @@ -2359,14 +2385,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, try { finishedCallback.onAnimationFinished(); } catch (RemoteException e) { - mLogger.logFailedToCallOnAnimationFinished(e); + Slog.e(TAG, "RemoteException"); } finally { mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION); } @@ -2531,7 +2558,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, try { finishedCallback.onAnimationFinished(); } catch (RemoteException e) { - mLogger.logFailedToCallOnAnimationFinished(e); + Slog.e(TAG, "RemoteException"); } finally { mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_UNLOCK_ANIMATION); } @@ -2600,9 +2627,11 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, * @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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, */ private void handleReset() { synchronized (KeyguardViewMediator.this) { - mLogger.logHandleReset(); + if (DEBUG) Log.d(TAG, "handleReset"); mKeyguardViewControllerLazy.get().reset(true /* hideBouncerWhenShowing */); } } @@ -2767,7 +2795,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private void handleNotifyStartedGoingToSleep() { synchronized (KeyguardViewMediator.this) { - mLogger.logHandleNotifyStartedGoingToSleep(); + if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep"); mKeyguardViewControllerLazy.get().onStartedGoingToSleep(); } } @@ -2787,7 +2815,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, */ private void handleNotifyFinishedGoingToSleep() { synchronized (KeyguardViewMediator.this) { - mLogger.logHandleNotifyFinishedGoingToSleep(); + if (DEBUG) Log.d(TAG, "handleNotifyFinishedGoingToSleep"); mKeyguardViewControllerLazy.get().onFinishedGoingToSleep(); } } @@ -2795,7 +2823,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // 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 class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 fdea62dc0cbe..56f1ac46a875 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.KeyguardQsUserSwitchComponent; 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 @@ public class KeyguardModule { InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, Lazy<NotificationShadeWindowController> notificationShadeWindowController, - Lazy<ActivityLaunchAnimator> activityLaunchAnimator, - KeyguardViewMediatorLogger logger) { + Lazy<ActivityLaunchAnimator> activityLaunchAnimator) { return new KeyguardViewMediator( context, falsingCollector, @@ -134,8 +132,7 @@ public class KeyguardModule { 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 77ad8069f273..6124e10144f2 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 @@ class LogBuffer @JvmOverloads constructor( * 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 684839f2b124..323ee21953ea 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 9af42f825e00..c2a87649adef 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 class LogModule { 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 012d76651b23..b02393b4f73a 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 @@ public class MediaControlPanel { 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 @@ public class MediaControlPanel { && 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 @@ public class MediaControlPanel { } 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 7b497ade683c..c48271e0348a 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 @@ class MediaDataManager( } 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 25186597e0e1..b3a4ddf8ec1f 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 @@ class MediaDeviceManager @Inject constructor( // 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 08cf57c7594e..b39770d302be 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 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements 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 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements 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 c5448713970c..acd04f216a98 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 androidx.annotation.Nullable; 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 @@ public class MediaDreamSentinel extends CoreStartable { } mAdded = false; - mDreamOverlayStateController.removeComplication(mComplication); + mDreamOverlayStateController.removeComplication(mMediaEntryComplication); } @Override @@ -79,24 +80,24 @@ public class MediaDreamSentinel extends CoreStartable { } 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 3408d97dd33f..052608f17e0e 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 @@ public interface MediaComplicationComponent { 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 00a22f20e94d..35a6c74518e0 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.View 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 @@ class MediaTttChipControllerReceiver @Inject constructor( // 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 a153cb6c0d31..f93c671f6740 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.logging.UiEventLogger 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 @@ enum class ChipStateSender( 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 @@ enum class ChipStateSender( 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 @@ enum class ChipStateSender( 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 933548963390..5ad82fd9fd8f 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.media.MediaRoute2Info 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 @@ class MediaTttChipControllerSender @Inject constructor( 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 @@ class MediaTttChipControllerSender @Inject constructor( 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 @@ class MediaTttChipControllerSender @Inject constructor( 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 @@ class MediaTttChipControllerSender @Inject constructor( // 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 000000000000..3373159fba4e --- /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 3f931088ec83..5da480968b89 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 java.util.Set; 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 @@ public class QSTileRevealController { }); } }; + 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 db7c1fd86be0..d2f3a6a7bee1 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 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C 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 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C 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 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C 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 60c8f3719a2e..1e51ffa292b7 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.Canvas 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 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a 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 a918e5d9e106..309059fdb9ad 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 @@ class RequestProcessor @Inject constructor( ) { 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 @@ class RequestProcessor @Inject constructor( 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 3580010cc1e8..f73d2041af95 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 @@ package com.android.systemui.screenshot import android.annotation.UserIdInt import android.content.ComponentName import android.graphics.Rect +import android.os.UserHandle import android.view.Display /** @@ -42,7 +43,7 @@ interface ScreenshotPolicy { 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 ba809f676f1e..c2a50609b6a5 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.content.Intent 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.CoroutineDispatcher 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 @@ internal class ScreenshotPolicyImpl @Inject constructor( } 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 @@ internal class ScreenshotPolicyImpl @Inject constructor( } 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)}" - ) - - 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 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]}") } } - 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 @@ internal class ScreenshotPolicyImpl @Inject constructor( } } - 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 @@ internal class ScreenshotPolicyImpl @Inject constructor( } } - 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 35f32caffe21..695a80b2b95d 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 @@ public class TakeScreenshotService extends Service { 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 9818af3751a7..1cdacb93ba45 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 @@ public class NotificationPanelView extends PanelView { 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 5f07f9f1c9ad..9a8395cdad68 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.GENERIC; 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 @@ import javax.inject.Provider; @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 @@ public final class NotificationPanelViewController { /** * 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 @@ public final class NotificationPanelViewController { 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 @@ public final class NotificationPanelViewController { 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 final class NotificationPanelViewController { } 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 4349d816b3c3..000000000000 --- 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 b9684fcb8a01..3d161d9bfa75 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.util.AttributeSet; 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 @@ public class EmptyShadeView extends StackScrollerDecorView { } @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 2ca1bebfcf9f..7b49ecdcd981 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 @@ import javax.inject.Inject */ @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 @@ class NotificationInteractionTracker @Inject constructor( 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 d1bc14fa52cf..d90824351c70 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 @@ public interface NotificationLockscreenUserManager { 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 @@ public interface NotificationLockscreenUserManager { 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 @@ public interface NotificationLockscreenUserManager { 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 f4ca7edb146d..ae5a2c3fe9e2 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.Log; 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 @@ public class NotificationLockscreenUserManagerImpl implements 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 @@ public class NotificationLockscreenUserManagerImpl implements } } - /** - * 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 @@ public class NotificationLockscreenUserManagerImpl implements /** * 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 @@ public class NotificationLockscreenUserManagerImpl implements } @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 221428747558..cea3debe6379 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.accessibility.AccessibilityNodeInfo; 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 @@ public class NotificationShelf extends ActivatableNotificationView implements } @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 8cb18a06057e..59022c0ffbf2 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 android.util.Pair; 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 @@ public class InstantAppNotifier extends CoreStartable 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 e2f87b6f4ffe..54be9a6ff39b 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 androidx.annotation.NonNull; 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 @@ import java.util.List; * 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 @@ public class NotificationEntryManager implements VisualStabilityManager.Callback 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 c9c6f28b5b27..000000000000 --- 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 dbf4810b4fd7..126a986ee5f4 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 @@ package com.android.systemui.statusbar.notification 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.PanelExpansionChangeEvent 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 @@ class NotificationWakeUpCoordinator @Inject constructor( 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 @@ class NotificationWakeUpCoordinator @Inject constructor( } init { - dumpManager.registerDumpable(this) mHeadsUpManager.addListener(this) statusBarStateController.addCallback(this) addListener(object : WakeUpListener { @@ -255,14 +248,13 @@ class NotificationWakeUpCoordinator @Inject constructor( // 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 @@ class NotificationWakeUpCoordinator @Inject constructor( // 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 @@ class NotificationWakeUpCoordinator @Inject constructor( */ 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 @@ class NotificationWakeUpCoordinator @Inject constructor( */ 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 @@ class NotificationWakeUpCoordinator @Inject constructor( */ @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 46b467e8962d..d52f3c692b83 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 @@ public interface NotificationRowBinder { */ 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 528f7203347f..47cdde444dbd 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 com.android.systemui.statusbar.notification.row.NotificationRowCon 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.dagger.SysUISingleton; 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 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { 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 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { 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 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { mRowInflaterTaskProvider = rowInflaterTaskProvider; mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder; mIconManager = iconManager; - mLowPriorityInflationHelper = lowPriorityInflationHelper; - mNotifPipelineFlags = notifPipelineFlags; } /** @@ -125,13 +117,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { @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 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { } /** - * 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 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { */ 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 89445a5c6467..000000000000 --- 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 d41f6fe7bdde..000000000000 --- 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 bb8c0e0d3b77..000000000000 --- 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 eda2eeca0620..9333c2ab3f55 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.os.Handler; 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.BindEven 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.NotificationSectionsMan 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 @@ public interface NotificationsModule { @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 @@ public interface NotificationsModule { AccessibilityManager accessibilityManager, HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, - NotificationEntryManager notificationEntryManager, PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, @@ -177,20 +160,6 @@ public interface NotificationsModule { @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 385fbb5ccdee..be9f133c9aeb 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.AnimatedImageNotificationMana 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 @@ class NotificationsControllerImpl @Inject constructor( 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 @@ class NotificationsControllerImpl @Inject constructor( ) { 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 000000000000..4cf3572b5bda --- /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 3af6ba8434a5..000000000000 --- 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 16574abab7aa..c17ffb0adacf 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 @@ import dagger.Module @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 class PeopleHubModule { 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 6062941d44a6..000000000000 --- 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 55bd77fdab68..000000000000 --- 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 855390d75ff8..8574f872ad9c 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.Animator; 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.Chronometer; 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @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 8f73b802271d..1e09b8a37645 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.ViewGroup; 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 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { 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 abstract class ExpandableView extends FrameLayout implements Dumpable { public void setActualHeightAnimating(boolean animating) {} + @NonNull protected ExpandableViewState createExpandableViewState() { return new ExpandableViewState(); } @@ -642,7 +644,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { 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 e43ecf70a308..49dc6550a51f 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.AttributeSet; 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 @@ public class FooterView extends StackScrollerDecorView { } @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 6abfee9818bc..000000000000 --- 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 79d883b32a98..1fb265fea0b1 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 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } 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 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @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 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } return row.canViewBeDismissed(); } - if (v instanceof PeopleHubView) { - return ((PeopleHubView) v).getCanSwipe(); - } return false; } @@ -6130,9 +6130,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } 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 3f6586c37b5d..ec1e6207ef8f 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.NotifPipeline; 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 @@ public class NotificationStackScrollLayoutController { 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 @@ public class NotificationStackScrollLayoutController { NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, CentralSurfaces centralSurfaces, ScrimController scrimController, - NotificationGroupManagerLegacy legacyGroupManager, GroupExpansionManager groupManager, @SilentHeader SectionHeaderController silentHeaderController, NotifPipeline notifPipeline, @@ -677,12 +673,6 @@ public class NotificationStackScrollLayoutController { 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 @@ public class NotificationStackScrollLayoutController { } /** - * 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 class NotificationStackScrollLayoutController { */ public void updateShowEmptyShadeView() { Trace.beginSection("NSSLC.updateShowEmptyShadeView"); - mShowEmptyShadeView = mStatusBarStateController.getCurrentOrUpcomingState() != KEYGUARD + + final boolean shouldShow = getVisibleNotificationCount() == 0 && !mView.isQsFullScreen() - && getVisibleNotificationCount() == 0; + // 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()); - mView.updateEmptyShadeView( - mShowEmptyShadeView, - 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 b13e7fb839ff..000000000000 --- 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 6d513d0da5c1..353355bef7bb 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 @@ public class StackScrollAlgorithm { 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 @@ public class StackScrollAlgorithm { } 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 @@ public class StackScrollAlgorithm { 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 747c4debb771..52a45d6785ca 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.DisplayId; 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 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba 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 ed186ab6a10b..8273d5737c2f 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 android.widget.LinearLayout.LayoutParams; 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 @@ public interface StatusBarIconController { 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 @@ public interface StatusBarIconController { 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 77654273bde4..103f3fc21f91 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.channels.awaitClose 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 @@ class WifiRepositoryImpl @Inject constructor( } } - 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 000000000000..7baebf4ef600 --- /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 000000000000..e627d610dc0a --- /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 13c3df3acb69..c7ba5182eef8 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.app.KeyguardManager; 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 @@ public class VolumeDialogImpl implements VolumeDialog, private final ConfigurationController mConfigurationController; private final MediaOutputDialogFactory mMediaOutputDialogFactory; + private final VolumePanelFactory mVolumePanelFactory; private final ActivityStarter mActivityStarter; private boolean mShowing; @@ -279,6 +279,7 @@ public class VolumeDialogImpl implements VolumeDialog, DeviceProvisionedController deviceProvisionedController, ConfigurationController configurationController, MediaOutputDialogFactory mediaOutputDialogFactory, + VolumePanelFactory volumePanelFactory, ActivityStarter activityStarter, InteractionJankMonitor interactionJankMonitor) { mContext = @@ -290,6 +291,7 @@ public class VolumeDialogImpl implements VolumeDialog, mDeviceProvisionedController = deviceProvisionedController; mConfigurationController = configurationController; mMediaOutputDialogFactory = mediaOutputDialogFactory; + mVolumePanelFactory = volumePanelFactory; mActivityStarter = activityStarter; mShowActiveStreamOnly = showActiveStreamOnly(); mHasSeenODICaptionsTooltip = @@ -1045,10 +1047,9 @@ public class VolumeDialogImpl implements VolumeDialog, 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 000000000000..2c74fb911688 --- /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 000000000000..f11d5d18ac84 --- /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 000000000000..c2fafbf9f55b --- /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 000000000000..23714021a2cc --- /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 f3855bddfe48..c5792b923e48 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.statusbar.policy.DeviceProvisionedController; 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 @@ public interface VolumeModule { DeviceProvisionedController deviceProvisionedController, ConfigurationController configurationController, MediaOutputDialogFactory mediaOutputDialogFactory, + VolumePanelFactory volumePanelFactory, ActivityStarter activityStarter, InteractionJankMonitor interactionJankMonitor) { VolumeDialogImpl impl = new VolumeDialogImpl( @@ -61,6 +63,7 @@ public interface VolumeModule { 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 199048ec7b2e..ce9674175bd5 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.NotifCollection; 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 @@ public class BubblesManager { 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 @@ public class BubblesManager { NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, @@ -148,7 +145,6 @@ public class BubblesManager { interruptionStateProvider, zenModeController, notifUserManager, - groupManager, notifCollection, notifPipeline, sysUiState, @@ -171,7 +167,6 @@ public class BubblesManager { NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, @@ -185,7 +180,6 @@ public class BubblesManager { mVisibilityProvider = visibilityProvider; mNotificationInterruptStateProvider = interruptionStateProvider; mNotifUserManager = notifUserManager; - mNotificationGroupManager = groupManager; mCommonNotifCollection = notifCollection; mNotifPipeline = notifPipeline; mSysuiMainExecutor = sysuiMainExecutor; @@ -331,16 +325,6 @@ public class BubblesManager { } @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 @@ public class BubblesManager { } @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 @@ public class BubblesManager { 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 aecec9d100cc..d68e8bd36c40 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 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } @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 000000000000..6b1ef389a98e --- /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 64a7986d05b1..df10dfe9f160 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 @@ package com.android.systemui; 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.junit.Assert.fail; 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.annotation.IdRes; 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.os.Handler; 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.keyguard.KeyguardUpdateMonitor; 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { @Mock private DisplayInfo mDisplayInfo; private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener; + @Mock + private CutoutDecorProviderFactory mCutoutFactory; + private List<DecorProvider> mMockCutoutList; @Before public void setup() { @@ -206,6 +209,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { } 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 e0d1f7a19130..b18b0abcaab1 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.BiometricAuthenticator.TYPE_FINGERPRIN 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 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private InteractionJankMonitor mInteractionJankMonitor; @Captor - ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor; + private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor; + @Captor + private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; @Captor - ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; + private ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor; @Captor - ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor; + private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; @Captor - ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; + private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefullnessObserverCaptor; private TestableContext mContextSpy; private Execution mExecution; @@ -242,7 +246,9 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { } @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); + 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); - // 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).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 3ac28c8d0d37..2af055783c22 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 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { 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 000000000000..1040ec453a7c --- /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 000000000000..bc944404efe6 --- /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 7d54758acee7..fa8f88a08368 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.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.Arrays; +import java.util.Collections; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -61,9 +61,6 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { private SmartSpaceComplication mComplication; @Mock - private ComplicationViewModel mComplicationViewModel; - - @Mock private View mBcSmartspaceView; @Before @@ -125,12 +122,12 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { // 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 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { 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 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { 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 ff579a1cc4aa..318f2bc1c227 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 @@ class FakeFeatureFlagsTest : SysuiTestCase() { * 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 @@ class FakeFeatureFlagsTest : SysuiTestCase() { } @Test - fun specifiedFlagsReturnCorrectValues() { + fun specifiedFlags_returnCorrectValues() { val flags = FakeFeatureFlags() flags.set(unreleasedFlag, false) flags.set(releasedFlag, false) @@ -114,4 +114,125 @@ class FakeFeatureFlagsTest : SysuiTestCase() { 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 6e89bb90e558..21c018a0419d 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.internal.widget.LockPatternUtils; 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 7b12eb75e841..56aff3c2fc8b 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 @@ class LogBufferTest : SysuiTestCase() { } @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 178502269e73..bef46953395b 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 @@ public class MediaControlPanelTest : SysuiTestCase() { } @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 d1ed8e983cdd..f9c7d2d5cb41 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.statusbar.SbnBuilder 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 @@ class MediaDataManagerTest : SysuiTestCase() { 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 @@ class MediaDataManagerTest : SysuiTestCase() { 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 @@ class MediaDataManagerTest : SysuiTestCase() { @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 @@ class MediaDataManagerTest : SysuiTestCase() { // 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 @@ class MediaDataManagerTest : SysuiTestCase() { 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 ee104262dc29..121c8946d164 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.reset 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 @@ public class MediaDeviceManagerTest : SysuiTestCase() { 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 @@ public class MediaDeviceManagerTest : SysuiTestCase() { 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 @@ public class MediaDeviceManagerTest : SysuiTestCase() { 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 @@ public class MediaDeviceManagerTest : SysuiTestCase() { 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 61736923fb82..9be201e99b2b 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 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { } @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 c101b9ffd495..2e864dc2cda8 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 @@ package com.android.systemui.media.dream; 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 androidx.test.filters.SmallTest; 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 @@ public class MediaDreamSentinelTest extends SysuiTestCase { DreamOverlayStateController mDreamOverlayStateController; @Mock - MediaDreamComplication mComplication; + DreamMediaEntryComplication mMediaEntryComplication; @Mock FeatureFlags mFeatureFlags; @@ -72,7 +74,7 @@ public class MediaDreamSentinelTest extends SysuiTestCase { @Test public void testComplicationAddition() { final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager, - mDreamOverlayStateController, mComplication, mFeatureFlags); + mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags); sentinel.start(); @@ -85,14 +87,16 @@ public class MediaDreamSentinelTest extends SysuiTestCase { 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 @@ public class MediaDreamSentinelTest extends SysuiTestCase { 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 1061e3c6b0d5..fa47a746f8ba 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.logging.testing.UiEventLoggerFake 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.Before 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 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { 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 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { accessibilityManager, configurationController, powerManager, - senderUiEventLogger + senderUiEventLogger, + falsingManager, + falsingCollector ) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) @@ -421,6 +430,38 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } @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 48fbd354b98d..073c23cec569 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.Insets 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 @@ class RequestProcessorTest { 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 @@ class RequestProcessorTest { // 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 @@ class RequestProcessorTest { 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 @@ class RequestProcessorTest { // 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 000000000000..17396b13036c --- /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 83e56daf1fbc..6ce9cff9a730 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.internal.util.ScreenshotHelper.ScreenshotRequest 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.any 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 @@ class TakeScreenshotServiceTest : SysuiTestCase() { .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 @@ class TakeScreenshotServiceTest : SysuiTestCase() { 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 @@ class TakeScreenshotServiceTest : SysuiTestCase() { 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 @@ class TakeScreenshotServiceTest : SysuiTestCase() { service.handleRequest(request, { /* onSaved */ }, callback) - verify(controller).takeScreenshotPartial( + verify(controller, times(1)).takeScreenshotPartial( /* topComponent = */ isNull(), /* onSavedListener = */ any(), /* requestCallback = */ any()) @@ -167,7 +205,7 @@ class TakeScreenshotServiceTest : SysuiTestCase() { 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 @@ class TakeScreenshotServiceTest : SysuiTestCase() { 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 @@ class TakeScreenshotServiceTest : SysuiTestCase() { 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 98389c2c7a6f..e2ce939cb66c 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 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { 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 dd2b66765fae..3cc4ee14b45f 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.SysuiTestCase; 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 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { 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 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { 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 3fc0c8176f2f..b719c7f9e54e 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 @@ package com.android.systemui.statusbar.notification; 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 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase { @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 19dd027daedd..000000000000 --- 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 c17fe6f8b7e2..000000000000 --- 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/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt deleted file mode 100644 index 5898664dea8e..000000000000 --- 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 b20f95cfad3a..000000000000 --- 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 1460e048d234..966e233fa410 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 static org.mockito.Mockito.when; 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.jank.InteractionJankMonitor; 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.NotificationRemoteInputManager; 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 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @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 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @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 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { 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 8fd6842911de..35d2363b1c2a 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 @@ package com.android.systemui.statusbar.notification.stack 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 @@ class StackScrollAlgorithmTest : SysuiTestCase() { 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 @@ class StackScrollAlgorithmTest : SysuiTestCase() { @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 @@ class StackScrollAlgorithmTest : SysuiTestCase() { stackScrollAlgorithm.resetViewStates(ambientState, 0) - assertThat(expandableViewState.yTranslation).isEqualTo(stackScrollAlgorithm.mHeadsUpInset) + assertThat(notificationRow.viewState.yTranslation) + .isEqualTo(stackScrollAlgorithm.mHeadsUpInset) } @Test @@ -75,15 +83,12 @@ class StackScrollAlgorithmTest : SysuiTestCase() { 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 @@ class StackScrollAlgorithmTest : SysuiTestCase() { } @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 @@ class StackScrollAlgorithmTest : SysuiTestCase() { 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 a0f7087ddb9e..735a88dc0ab7 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.NotifLiveDataStore 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 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @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 a6b7e5103c78..ca98143044c1 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 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { @Before public void setup() { injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); - mDependency.injectMockDependency(DarkIconDispatcher.class); } @Test @@ -75,7 +74,8 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { layout, mock(FeatureFlags.class), mock(StatusBarPipelineFlags.class), - () -> mock(WifiViewModel.class)); + () -> mock(WifiViewModel.class), + mock(DarkIconDispatcher.class)); testCallOnAdd_forManager(manager); } @@ -116,8 +116,10 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { 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 982927148a2e..d070ba0e47be 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 @@ class WifiRepositoryImplTest : SysuiTestCase() { 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 000000000000..092e82c526e3 --- /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 312db2d7066a..2e74bf5474f9 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 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Mock MediaOutputDialogFactory mMediaOutputDialogFactory; @Mock + VolumePanelFactory mVolumePanelFactory; + @Mock ActivityStarter mActivityStarter; @Mock InteractionJankMonitor mInteractionJankMonitor; @@ -102,6 +104,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { 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 5d63632725c2..09da52e7685c 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.NotifPipelineFlags; 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 @@ public class BubblesTest extends SysuiTestCase { @Mock private CommonNotifCollection mCommonNotifCollection; @Mock - private NotificationGroupManagerLegacy mNotificationGroupManager; - @Mock private BubblesManager.NotifCallback mNotifCallback; @Mock private WindowManager mWindowManager; @@ -389,7 +386,6 @@ public class BubblesTest extends SysuiTestCase { 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 b53ad0a3726f..c56fdb17b5f1 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 @@ class FakeFeatureFlags : FeatureFlags { } 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 @@ class FakeFeatureFlags : FeatureFlags { 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 6a6d2bb44d48..2b858ad045d8 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 f731c440d5f3..d20fa8ecebdf 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 @@ class MultiFingerSwipe extends GestureMatcher { 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 907daa34826f..aec5f5ef9ff9 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 @@ public class UserBackupManagerService { + " 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 5dacdb40713b..d0300ff19178 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 static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 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.android.server.backup.transport.TransportConnection; import com.google.android.collect.Sets; +import java.util.Arrays; import java.util.Set; /** @@ -360,8 +361,8 @@ public class BackupEligibilityRules { } 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 3d8dc148b7e2..bd1ecb23611e 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1305,4 +1305,7 @@ public abstract class PackageManagerInternal { * {@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 6aa472fcd586..a580f985dd13 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 13a13f2f4b3c..5e7d814b241b 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 @@ final class ActivityManagerConstants extends ContentObserver { 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 @@ final class ActivityManagerConstants extends ContentObserver { */ 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 @@ final class ActivityManagerConstants extends ContentObserver { 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 @@ final class ActivityManagerConstants extends ContentObserver { 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 @@ final class ActivityManagerConstants extends ContentObserver { } } + 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 @@ final class ActivityManagerConstants extends ContentObserver { 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 be0335e93d40..bd776c1d9933 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub mPendingStartActivityUids = new PendingStartActivityUids(); mUseFifoUiScheduling = false; mEnableOffloadQueue = false; + mBroadcastQueues = new BroadcastQueue[0]; mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue = mFgOffloadBroadcastQueue = null; mComponentAliasResolver = new ComponentAliasResolver(this); @@ -2401,6 +2410,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // !!! 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * 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 36908ce8c7b3..a42b2a48f886 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 e9a36e05c6c1..4c443433d396 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 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { // 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 @@ public class BroadcastDispatcher { // 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 @@ public class BroadcastDispatcher { synchronized (mLock) { return mCurrentBroadcast == null && mOrderedBroadcasts.isEmpty() + && mAlarmQueue.isEmpty() && isDeferralsListEmpty(mDeferredBroadcasts) - && isDeferralsListEmpty(mAlarmBroadcasts); + && isDeferralsListEmpty(mAlarmDeferrals); } } @@ -557,7 +564,13 @@ public class BroadcastDispatcher { } 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 @@ public class BroadcastDispatcher { // ---------------------------------- // 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 @@ public class BroadcastDispatcher { return; } } else { - mOrderedBroadcasts.add(r); + // Ordinary broadcast, so put it on the appropriate queue and carry on + queue.add(r); } } @@ -652,10 +674,13 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { packageName, filterByClasses, userId, doit); } if (doit || !didSomething) { - didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts, + didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmDeferrals, packageName, filterByClasses, userId, doit); } if (doit || !didSomething) { @@ -781,12 +810,15 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { * immediately deliverable. Used by the wait-for-broadcast-idle mechanism. */ public void cancelDeferralsLocked() { - zeroDeferralTimes(mAlarmBroadcasts); + zeroDeferralTimes(mAlarmDeferrals); zeroDeferralTimes(mDeferredBroadcasts); } @@ -1023,7 +1065,7 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { 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 @@ public class BroadcastDispatcher { } else { pw.println(" (null)"); } + printed |= dumper.didPrint(); + + 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 : mAlarmBroadcasts) { + for (Deferrals d : mAlarmDeferrals) { d.dumpLocked(dumper); } - printed |= dumper.didPrint(); - 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 9be22c0e41c0..752c6b683e05 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.Handler; 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 @@ import java.util.Set; 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; - - BroadcastQueue(ActivityManagerService service, Handler handler, - String name, BroadcastConstants constants) { - mService = service; - mHandler = handler; - mQueueName = name; - mConstants = constants; - mSkipPolicy = new BroadcastSkipPolicy(service); + final @NonNull ActivityManagerService mService; + final @NonNull Handler mHandler; + final @NonNull BroadcastConstants mConstants; + final @NonNull BroadcastSkipPolicy mSkipPolicy; + final @NonNull String mQueueName; + + 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 class BroadcastQueue { 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 @@ public abstract class BroadcastQueue { * 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 @@ public abstract class BroadcastQueue { * <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 @@ public abstract class BroadcastQueue { /** * 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 class BroadcastQueue { */ 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 2b3b21182a6c..d9146121fc27 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.NonNull; 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 @@ public class BroadcastQueueImpl extends BroadcastQueue { 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 class BroadcastQueueImpl extends BroadcastQueue { 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 @@ public class BroadcastQueueImpl extends BroadcastQueue { }, 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 @@ public class BroadcastQueueImpl extends BroadcastQueue { // 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 9abd01ac3ee9..78629b04cff2 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 @@ public class ContentProviderHelper { 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 baf062d28457..6087f76687bf 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 @@ package com.android.server.am; 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 @@ public class DropboxRateLimiter { 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 2498f763ff77..30811a175bac 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__HO 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 final class HostingRecord { 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 final class HostingRecord { } 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 @@ public final class HostingRecord { 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 ccbca76d1868..48eb0de9011f 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.SystemConfig; 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 @@ public final class ProcessList { 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 de70fa0ff2be..dcadd5ff518c 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.PendingIntent; 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 com.android.server.infra.AbstractPerUserSystemService; 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 @@ final class AmbientContextManagerPerUserService extends * 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 @@ final class AmbientContextManagerPerUserService extends * 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 @@ final class AmbientContextManagerPerUserService extends 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 @@ final class AmbientContextManagerPerUserService extends 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 @@ final class AmbientContextManagerPerUserService extends mRemoteService.queryServiceStatus( eventTypes, callingPackage, - getServerStatusCallback(statusCallback)); + getServerStatusCallback( + statusCode -> sendStatusCallback(statusCallback, statusCode))); } } @@ -350,18 +357,6 @@ final class AmbientContextManagerPerUserService extends 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 @@ final class AmbientContextManagerPerUserService extends * 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 @@ final class AmbientContextManagerPerUserService extends 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 @@ final class AmbientContextManagerPerUserService extends (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 4206262398c7..e205e84cea26 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.AmbientContextEvent; 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 com.android.server.pm.KnownPackages; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -67,31 +70,27 @@ public class AmbientContextManagerService extends 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 @@ public class AmbientContextManagerService extends } 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 @@ public class AmbientContextManagerService extends } @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 @@ public class AmbientContextManagerService extends * 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 @@ public class AmbientContextManagerService extends Slog.d(TAG, "Restoring detection for " + clientRequest.getPackageName()); service.startDetection(clientRequest.getRequest(), clientRequest.getPackageName(), - service.createDetectionResultRemoteCallback(), - clientRequest.getClientStatusCallback()); + clientRequest.getObserver()); } } } @@ -328,16 +324,45 @@ public class AmbientContextManagerService extends 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 @@ public class AmbientContextManagerService extends 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 ec6c2f00b3b1..a3ffcde802f3 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.annotation.NonNull; 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 @@ final class AmbientContextShellCommand extends ShellCommand { new AmbientContextEventRequest.Builder() .addEventType(AmbientContextEvent.EVENT_COUGH) .addEventType(AmbientContextEvent.EVENT_SNORE) + .addEventType(AmbientContextEvent.EVENT_BACK_DOUBLE_TAP) .build(); @NonNull @@ -50,11 +53,11 @@ final class AmbientContextShellCommand extends ShellCommand { /** 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 @@ final class AmbientContextShellCommand extends ShellCommand { } @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 class AmbientContextShellCommand extends ShellCommand { 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 158092f33ee3..dd0c4b86a77d 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 @@ final class DiscreteRegistry { } } 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 66a4527bc2cb..4adbfaa79c69 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 @@ public class BtHelper { 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 @@ public class BtHelper { 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 96c562100a7a..76eba1699298 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.security.NoSuchAlgorithmException; 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 @@ public class BackupUtils { } 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 689ddd2fb9ee..c29755aaa845 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.os.SomeArgs; 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 @@ public class BiometricService extends SystemService { 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 @@ public class BiometricService extends SystemService { 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 @@ public class BiometricService extends SystemService { 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 000000000000..62f94ed05e0a --- /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 c86a8cb2c39d..8265203ddacd 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 @@ public interface BiometricContext { /** 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 9d2fde72ea2e..3d1a6343cbf2 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 @@ import java.util.function.Consumer; /** * 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 @@ class BiometricContextProvider implements BiometricContext { 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 @@ class BiometricContextProvider implements BiometricContext { 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 @@ class BiometricContextProvider implements BiometricContext { notifySubscribers(); } } + + private boolean isAodEnabled() { + return mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT); + } }); service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() { @Override @@ -161,7 +171,12 @@ class BiometricContextProvider implements BiometricContext { @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 262be08b60e2..02b350e97ef8 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 class BiometricLogger { 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 @@ public class BiometricLogger { } /** + * 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 @@ public class BiometricLogger { 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 @@ public class BiometricLogger { /** 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 @@ public class BiometricLogger { + ", RequireConfirmation: " + requireConfirmation + ", State: " + authState + ", Latency: " + latency - + ", Lux: " + mLastAmbientLux); + + ", Lux: " + mALSProbe.getCurrentLux()); } else { Slog.v(TAG, "Authentication latency: " + latency); } @@ -276,7 +231,7 @@ public class BiometricLogger { 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 @@ public class BiometricLogger { + ", User: " + targetUserId + ", Client: " + mStatsClient + ", Latency: " + latency - + ", Lux: " + mLastAmbientLux + + ", Lux: " + mALSProbe.getCurrentLux() + ", Success: " + enrollSuccessful); } else { Slog.v(TAG, "Enroll latency: " + latency); @@ -301,7 +256,7 @@ public class BiometricLogger { } 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 @@ public class BiometricLogger { } /** - * 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 @@ public class BiometricLogger { * @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 4eb6d38d9227..8a24ff6cffde 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.BiometricManager; 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 @@ import java.util.function.Supplier; * 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 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> 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 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> 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 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> 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 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> 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); - } else { - Slog.d(TAG, "Skipping addAuthToken"); - } - - 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"); - } + // 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); } - - @Override - public void sendHapticFeedback() { - if (listener != null && mShouldVibrate) { - vibrateSuccess(); + } else { + Slog.d(TAG, "Skipping addAuthToken"); + } + try { + if (listener != null) { + if (!mIsRestricted) { + listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken, + getTargetUserId(), mIsStrongBiometric); + } else { + listener.onAuthenticationSucceeded(getSensorId(), null /* identifier */, + byteToken, + getTargetUserId(), mIsStrongBiometric); } + } else { + Slog.e(TAG, "Received successful auth, but client was not listening"); } - - @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); - } - }); - } - } - } - - /** - * 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(); + try { + listener.onAuthenticationFailed(getSensorId()); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify listener", e); + mCallback.onClientFinished(this, false); + return; } } - }); + } + AuthenticationClient.this.handleLifecycleAfterAuth(authenticated); } private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) { @@ -396,7 +310,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> 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 abstract class AuthenticationClient<T> extends AcquisitionClient<T> 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 abstract class AuthenticationClient<T> extends AcquisitionClient<T> 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 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } /** - * 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 63609f77dc75..9317c4ec12b5 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 @@ import java.util.function.Consumer; * 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 @@ public class BiometricScheduler { 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 @@ public class BiometricScheduler { @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 @@ public class BiometricScheduler { } 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 @@ public class BiometricScheduler { @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 @@ public class BiometricScheduler { mCrashStates = new ArrayDeque<>(); mRecentOperationsLimit = recentOperationsLimit; mRecentOperations = new ArrayList<>(); - mCoexCoordinator = coexCoordinator; } /** @@ -244,7 +232,7 @@ public class BiometricScheduler { 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 c8a90e7a564b..000000000000 --- 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 ae75b7dcc101..a486d16189fa 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 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { @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 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { 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 d0c58fd0545f..e18e31ec33ea 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 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> @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 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> @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 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> @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 da7853654ebf..5d62cde01a6f 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 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> { @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 1935a5b135d7..9baca98e4d2b 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 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { @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 226e458ad07b..16d2f7a03c6d 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 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { @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 b530c8db3c67..f7d94c9c2b9f 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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> 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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> 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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> try { mIsPointerDown = true; mState = STATE_STARTED; - mALSProbeCallback.getProbe().enable(); final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { @@ -389,7 +394,6 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> try { mIsPointerDown = false; mState = STATE_STARTED_PAUSED_ATTEMPTED; - mALSProbeCallback.getProbe().disable(); final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { @@ -424,7 +428,6 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> @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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> @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 f4f0a19d0db7..e0393b54d2c5 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 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps 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 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps 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 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps 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 97fbb5f6e4fe..7ed1a514f9bc 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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi 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 2a59c8c0f204..5d9af5322c2e 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 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @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 25d0752844fd..c835d2fe1bbd 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 @@ public abstract class BrightnessMappingStrategy { 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 000000000000..e9640cf6b4d5 --- /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 3b627ef6a786..4f3fd6409cd8 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.annotation.NonNull; 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 @@ import javax.xml.datatype.DatatypeConfigurationException; * </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 @@ public class DisplayDeviceConfig { // 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 @@ public class DisplayDeviceConfig { 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 @@ public class DisplayDeviceConfig { + mAutoBrightnessBrighteningLightDebounce + ", mAutoBrightnessDarkeningLightDebounce= " + mAutoBrightnessDarkeningLightDebounce - + ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux) - + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits) + "}"; } @@ -839,7 +779,6 @@ public class DisplayDeviceConfig { loadBrightnessRampsFromConfigXml(); loadAmbientLightSensorFromConfigXml(); setProxSensorUnspecified(); - loadAutoBrightnessConfigsFromConfigXml(); mLoadedFrom = "<config.xml>"; } @@ -1052,7 +991,6 @@ public class DisplayDeviceConfig { private void loadAutoBrightnessConfigValues(DisplayConfiguration config) { loadAutoBrightnessBrighteningLightDebounce(config.getAutoBrightness()); loadAutoBrightnessDarkeningLightDebounce(config.getAutoBrightness()); - loadAutoBrightnessDisplayBrightnessMapping(config.getAutoBrightness()); } /** @@ -1085,33 +1023,6 @@ public class DisplayDeviceConfig { } } - /** - * 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 @@ public class DisplayDeviceConfig { 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 @@ public class DisplayDeviceConfig { } } - /** - * 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 330379cf58eb..b0de844389b6 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 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mSurface.release(); mSurface = null; } - SurfaceControl.destroyDisplay(getDisplayTokenLocked()); + DisplayControl.destroyDisplay(getDisplayTokenLocked()); } @Override @@ -460,7 +460,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { 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 479629eae7ac..38728ce106af 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 @@ public class VirtualDisplayAdapter extends DisplayAdapter { // 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 @@ public class VirtualDisplayAdapter extends DisplayAdapter { 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 d632ee3d021c..146b003650ac 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 6f890cda5964..dc1f4ddb19b9 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 @@ class GnssNetworkConnectivityHandler { 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 04cacee32283..cbbb3362a387 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 com.android.server.SystemService; 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 @@ public final class LogcatManagerService extends SystemService { * 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 @@ public final class LogcatManagerService extends SystemService { } 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 9588fa9d1f8e..87d30f3dd537 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 c12dc8e9e92e..9a190316f4eb 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.Handler; 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 @@ final class MediaButtonReceiverHolder { 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 4f8771a3ddef..34cd6a012925 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.Process; 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } @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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR 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 class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR 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 5599b0c07377..8ce7b57c55e0 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.provider.Settings; 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.File; 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 @@ class WatchlistLoggingHandler extends Handler { 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 6cfe093df6d0..4b6543b54f2f 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.Installer.InstallerException; 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 @@ public class PackageDexOptimizer { */ 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 @@ public class PackageDexOptimizer { * 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 e969d93465f8..330e3313883c 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 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { 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 297439f33a8b..8f89fdd40b32 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 @@ public abstract class UserManagerInternal { * {@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 d8b7fdab70f7..3b898f876072 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 @@ public class UserManagerService extends IUserManager.Stub { 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 class UserManagerService extends IUserManager.Stub { 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 17109e97ddf1..8c2da45da994 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.FileUtils; 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.io.IOException; 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 @@ public class DexManager { 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 @@ public class DexManager { } /** - * 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 1a1de0341aa0..79fe9050e0d4 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 android.view.autofill.AutofillManagerInternal; 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.io.PrintWriter; 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; + private Supplier<GlobalActions> mGlobalActionsFactory; private GlobalActions mGlobalActions; private Handler mHandler; @@ -1544,7 +1547,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 5964fa49f035..ebeb145526a4 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 @@ public class LowPowerStandbyController { 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 @@ public class LowPowerStandbyController { } @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 09035cd1d165..15c9ba923d3a 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.IBinder; 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 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware @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); + } + } + 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)); } - // Detach. + 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 d7bfd1deca90..fcf0a904b808 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 @@ final class VibrationSettings { } }); - 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 c04b19558770..804689a219b4 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 @@ final class AccessibilityController { 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 f8f94f655a16..63dcbf85ed32 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_DEFAULT; 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + /** 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A }); } + /** 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * 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 619d693068d4..9ad62af2d417 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 @@ class ActivityStarter { 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 1c90bbae79ec..6ee018606d71 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp 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 508e6dc77a61..d4eac8d3ad4b 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { } /** + * @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 @@ public class DisplayPolicy { // 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 e38f5fe61888..659e37bb3de7 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_CHANGE; 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 @@ class Task extends TaskFragment { 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 @@ class Task extends TaskFragment { 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 b8d2febd5dd4..09fd90030f0c 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.FileWriter; 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 @@ public class TaskPersister implements PersisterQueue.Listener { 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 8025cb296b32..8aa58b250bb3 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 @@ public class WindowManagerService extends IWindowManager.Stub prepareNoneTransitionForRelaunching(activity); } - if (displayPolicy.areSystemBarsForcedShownLw()) { + if (displayPolicy.areSystemBarsForcedConsumedLw()) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; } @@ -2527,7 +2527,7 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub : 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 346930311068..2e7e78db9e05 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 40789964f4e1..5d6ffd8b8ead 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -37,6 +37,7 @@ cc_library_static { "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_library_static { 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 @@ cc_defaults { "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 000000000000..61f2b1411a0b --- /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 0cd949457cd1..00bef0935308 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -64,6 +64,7 @@ int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env); 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 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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 000000000000..477625a36cbd --- /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 98f83d8c0d09..bea5e2c2de74 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 e5d26177b725..e9a926946764 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 @@ package com.android.server.display.config { 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 @@ package com.android.server.display.config { 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 f454ac7e9e4b..f1dc1fa1a108 100644 --- a/services/tests/mockingservicestests/jni/Android.bp +++ b/services/tests/mockingservicestests/jni/Android.bp @@ -10,6 +10,8 @@ package { cc_library_shared { name: "libmockingservicestestjni", + defaults: ["android.hardware.graphics.common-ndk_shared"], + cflags: [ "-Wall", "-Werror", @@ -48,7 +50,6 @@ cc_library_shared { "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 000000000000..130b02d3989e --- /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 03eff2a1831b..e170e98dd747 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 @@ public final class GameServiceControllerTest { 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 e4f9eaf091e9..3f16a98c81c9 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 com.google.common.truth.Truth.assertThat; 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.anyString; 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 org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.HashMap; -import java.util.Objects; import java.util.function.Consumer; @@ -361,7 +358,6 @@ public final class GameServiceProviderInstanceImplTest { throws Exception { mGameServiceProviderInstance.start(); - mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty(); @@ -383,7 +379,6 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); - mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation = @@ -398,7 +393,6 @@ public final class GameServiceProviderInstanceImplTest { 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 final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { startTask(10, GAME_A_MAIN_ACTIVITY); - mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -677,7 +660,6 @@ public final class GameServiceProviderInstanceImplTest { 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 final class GameServiceProviderInstanceImplTest { 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 final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); - mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); dispatchTaskRemoved(10); @@ -782,7 +760,6 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { } @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 @@ public final class GameServiceProviderInstanceImplTest { 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 final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { @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 @@ public final class GameServiceProviderInstanceImplTest { 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 @@ public final class GameServiceProviderInstanceImplTest { @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 @@ public final class GameServiceProviderInstanceImplTest { .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 @@ public final class GameServiceProviderInstanceImplTest { } } - 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 @@ public final class GameServiceProviderInstanceImplTest { } 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 60ddeeb497a8..7755552bcad2 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 @@ public class LocalDisplayAdapterTest { .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 9d269719db64..fb9cbb00255c 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 org.mockito.quality.Strictness; 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 @@ public class DexManagerTests { 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 59b69f9ffebf..233caf9c7761 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 @@ public class MotionEventInjectorTest { @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 6bb494d3a92a..c75abf84fb89 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 static com.google.common.truth.Truth.assertThat; 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 androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.List; + /** * Unit test for {@link AmbientContextManagerService}. * atest FrameworksServicesTests:AmbientContextManagerServiceTest @@ -48,12 +50,22 @@ public class AmbientContextManagerServiceTest { 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 000000000000..10f0a5cbbc40 --- /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 e6acc904d811..dd7aeb72f7d2 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.logging.InstanceId; 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 @@ public class BiometricContextProviderTest { @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).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).containsExactlyElementsIn(expected).inOrder(); + assertThat(actual).containsExactly(true, false, true, false).inOrder(); } @Test @@ -124,13 +153,13 @@ public class BiometricContextProviderTest { 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 @@ public class BiometricContextProviderTest { @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 @@ public class BiometricContextProviderTest { 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 0b8e8adc5cc4..60dc2eb6081d 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 class BiometricLoggerTest { 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 @@ public class BiometricLoggerTest { } @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 class BiometricLoggerTest { 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 45e3b4373266..eb1314194aa3 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 @@ public class BiometricSchedulerTest { 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 abf992b6c637..000000000000 --- 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 0df3028805d2..0815fe52e262 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 @@ public class UserAwareBiometricSchedulerTest { 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 b60324e88f15..518946aa761a 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 androidx.test.filters.SmallTest; 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 @@ public class SensorTest { 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 a149f3ee3f53..dea4d4fb7c64 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.anyLong; 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 @@ public class FingerprintAuthenticationClientTest { InstrumentationRegistry.getInstrumentation().getTargetContext(), null); @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Mock private ISession mHal; @Mock @@ -137,7 +138,7 @@ public class FingerprintAuthenticationClientTest { @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 @@ public class FingerprintAuthenticationClientTest { } @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 f77eb0bcc59f..92e1f27ab624 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.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.ArrayList; import java.util.function.Consumer; @Presubmit @@ -119,7 +120,7 @@ public class FingerprintEnrollClientTest { @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 @@ public class FingerprintEnrollClientTest { } @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); + client.onPointerUp(); + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); verify(mLuxProbe, never()).disable(); + verify(mLuxProbe, never()).destroy(); - client.onPointerUp(); - verify(mLuxProbe).disable(); + client.onEnrollResult(new Fingerprint("f", 30 /* fingerId */, 14 /* deviceId */), + 0 /* remaining */); - client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); - verify(mLuxProbe, times(2)).enable(); + 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 e1a4a2d9f969..ff636c840bad 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 androidx.test.filters.SmallTest; 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 @@ public class SensorTest { 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 261b882319d8..03ea6137074d 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 @@ package com.android.server.display; 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 @@ public final class DisplayDeviceConfigTest { 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 @@ public final class DisplayDeviceConfigTest { 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 @@ public final class DisplayDeviceConfigTest { + "<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 @@ public final class DisplayDeviceConfigTest { 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 f33001774263..6a27f391ac5b 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 static org.mockito.Mockito.when; 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 @@ public class LogcatManagerServiceTest { @Mock private ActivityManagerInternal mActivityManagerInternalMock; @Mock + private PackageManager mPackageManagerMock; + @Mock private ILogd mLogdMock; private LogcatManagerService mService; @@ -81,10 +86,17 @@ public class LogcatManagerServiceTest { 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 @@ public class LogcatManagerServiceTest { } @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 000000000000..62875e5863b3 --- /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 index cf571815b4cb..4c698571db16 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 @@ import java.util.concurrent.TimeUnit; * 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 @@ public class KeyCombinationTests { 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 000000000000..7c6fd05bcd18 --- /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 000000000000..6368f4748b26 --- /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 000000000000..9d57f48c064f --- /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 44494831eb68..25c8f145bd62 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 @@ public class ActivityRecordTests extends WindowTestsBase { 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 @@ public class ActivityRecordTests extends WindowTestsBase { 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 @@ public class ActivityRecordTests extends WindowTestsBase { // 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 6d33aaffdf24..17387061a86e 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 @@ public class SizeCompatTests extends WindowTestsBase { } @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 f169926fb3ac..1b34b817def1 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 @@ public class AppTimeLimitController { 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 @@ public class AppTimeLimitController { 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 9fcf44e36337..ccec67e8687e 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 @@ public class TelecomManager { 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 @@ public class TelecomManager { 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 677fe2f188a9..62eac7ac942a 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 @@ public class PduParser { 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 d978f5701eca..212aaaed46b2 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -433,7 +433,8 @@ public final class PhysicalChannelConfig implements Parcelable { 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 ae7d20929d58..3c1824539d1f 100644 --- a/telephony/java/android/telephony/SignalThresholdInfo.java +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -574,8 +574,8 @@ public final class SignalThresholdInfo implements Parcelable { @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 cbd03c7f653a..d670e5592c42 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -1935,7 +1935,7 @@ public final class SmsManager { + " 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 @@ public final class SmsManager { + " 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 c36eb2f9cf65..cb985bf2cda5 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -967,9 +967,9 @@ public class SubscriptionInfo implements Parcelable { 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 e0c529848aae..19f2a9bc67a3 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -171,7 +171,7 @@ public final class TelephonyScanManager { 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 3853af2f609e..1a40f82654ff 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 @@ class OpenActivityEmbeddingPlaceholderSplit( 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 9cc1bfe81c8c..ec2b4fa35c41 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 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio @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 58a80112c85e..55d412927ba6 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 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio @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 f6f3f58f29cb..725c10a8ada0 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 @@ class CloseImeAutoOpenWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest 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 52f561ec4497..8832686b43aa 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 @@ class CloseImeAutoOpenWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTes @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 c6e25d3de4cd..71e0aa1f9628 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 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t 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 23bd2200397a..0f91fd58abb7 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 @@ class CloseImeWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest(testSpe @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 8ce184072d32..007a4f1835d7 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 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp 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 a04a50f02c26..216e0edaf451 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 @@ class LaunchAppShowImeAndDialogThemeAppTest( @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 04e4bc94de9c..868290ec7585 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 @@ class LaunchAppShowImeOnStartTest(testSpec: FlickerTestParameter) : BaseTest(tes 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 b10aed30e39e..16c23b93a5de 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 @@ class OpenImeWindowAndCloseTest(testSpec: FlickerTestParameter) : BaseTest(testS 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 d9008158ffaf..e5874921ddfa 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 @@ class OpenImeWindowFromFixedOrientationAppTest( 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 fdc2193b7eb8..c1f17f3deb2e 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 @@ class OpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { 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 9475734ef3f5..5fd94427dc82 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 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes 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 2e22e6224813..0281a60bbc3b 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 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp 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 4f47ec439da8..85bf6d752bf5 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 @@ open class SwitchImeWindowsFromGestureNavTest( 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 33c280ea78bb..eb9acc4b8e4e 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 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp @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 bfc7b39f9d9f..b3db5b70fafa 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 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) : @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 f93d7a0b09e3..8c1d244b69e0 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 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) : @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 75311eaf5c66..caf2e2dbadc6 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 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet @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 dbe541882fa8..e744d44bc542 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 @@ open class OpenAppFromNotificationCold( @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 915b70289055..4ea42433e054 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 @@ open class OpenAppFromNotificationWarm( @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 7c07ace06b82..a3dd0cbcf64f 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 @@ open class OpenAppFromOverviewTest( @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 53be7d43cce8..82e30accb341 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 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : 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 fe5e74b87f93..5f342a00bf8c 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 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { @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 181767b3448d..f85bad33c4fd 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 @@ open class QuickSwitchBetweenTwoAppsBackTest( 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 0f05622c81bc..f6392cab4a35 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 @@ open class QuickSwitchBetweenTwoAppsForwardTest( 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 d1f356c830eb..a714111fae21 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 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes 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 4be8963bf7b7..e6c1eaca9380 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 @@ class ChangeAppRotationTest( @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 0912812afef9..07c213034642 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 @@ open class SeamlessAppRotationTest( @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 8b69db707d41..dc34cb6d6a53 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 class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { @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 @@ public class HandwritingIme extends InputMethodService { 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 c9e429b6f4c8..94b1f863f197 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.WindowManager; 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 9f6ce4e8425b..b7c4c5b28168 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -61,6 +61,7 @@ java_test_host { 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 1709e1501537..ffde8c7d342c 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -58,6 +58,7 @@ java_test_host { ":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 db4898492ac5..661dd845757b 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.CheckBox; 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 @@ public class MainInteractionSession extends VoiceInteractionSession @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 733f602f6c14..8ae7186461d3 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.view.View; 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 @@ public class StartVoiceInteractionActivity extends Activity implements View.OnCl } @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 ada0e21e059a..4fc3a15ac38d 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.view.View; 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 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis } @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 dfa229173373..f9e52b491413 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 @@ class XmlPrinter : public xml::ConstVisitor { } 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 46a846bce35f..4fb7ed19ed20 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -369,9 +369,13 @@ class PackageFlattener { 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 e48fca61fef8..f551bf61dc06 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -330,7 +330,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) { 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,26 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionO) { .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; + 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()); +} + +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; @@ -389,6 +408,27 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionO) { 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) { |