diff options
469 files changed, 8508 insertions, 5702 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index ce3e985e22d5..b54022bf47f2 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -64,6 +64,7 @@ aconfig_srcjars = [ ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}", ":android.tracing.flags-aconfig-java{.generated_srcjars}", ":android.appwidget.flags-aconfig-java{.generated_srcjars}", + ":android.webkit.flags-aconfig-java{.generated_srcjars}", ] filegroup { @@ -762,3 +763,19 @@ java_aconfig_library { aconfig_declarations: "android.appwidget.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// WebView +aconfig_declarations { + name: "android.webkit.flags-aconfig", + package: "android.webkit", + srcs: [ + "core/java/android/webkit/*.aconfig", + "services/core/java/com/android/server/webkit/*.aconfig", + ], +} + +java_aconfig_library { + name: "android.webkit.flags-aconfig-java", + aconfig_declarations: "android.webkit.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Ravenwood.bp b/Ravenwood.bp index 03a23ba15273..ca73378c8e81 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -32,7 +32,6 @@ java_genrule { cmd: "$(location hoststubgen) " + "@$(location ravenwood/ravenwood-standard-options.txt) " + - "--out-stub-jar $(location ravenwood_stub.jar) " + "--out-impl-jar $(location ravenwood.jar) " + "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " + @@ -49,7 +48,6 @@ java_genrule { ], out: [ "ravenwood.jar", - "ravenwood_stub.jar", // It's not used. TODO: Update hoststubgen to make it optional. // Following files are created just as FYI. "hoststubgen_keep_all.txt", diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp index 9366ff2d81a9..e1b3241e051e 100644 --- a/apct-tests/perftests/core/Android.bp +++ b/apct-tests/perftests/core/Android.bp @@ -66,6 +66,7 @@ android_test { errorprone: { javacflags: [ "-Xep:ReturnValueIgnored:WARN", + "-Xep:UnnecessaryStringBuilder:OFF", ], }, } diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java index c42c7ca25133..2ace6028456e 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java @@ -128,6 +128,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration(); final InsetsState mOutInsetsState = new InsetsState(); final InsetsSourceControl.Array mOutControls = new InsetsSourceControl.Array(); + final Bundle mOutBundle = new Bundle(); final IWindow mWindow; final View mView; final WindowManager.LayoutParams mParams; @@ -136,7 +137,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase final SurfaceControl mOutSurfaceControl; final IntSupplier mViewVisibility; - + int mRelayoutSeq; int mFlags; RelayoutRunner(Activity activity, IWindow window, IntSupplier visibilitySupplier) { @@ -152,10 +153,11 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase void runBenchmark(BenchmarkState state) throws RemoteException { final IWindowSession session = WindowManagerGlobal.getWindowSession(); while (state.keepRunning()) { + mRelayoutSeq++; session.relayout(mWindow, mParams, mWidth, mHeight, - mViewVisibility.getAsInt(), mFlags, 0 /* seq */, 0 /* lastSyncSeqId */, + mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */, mOutFrames, mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, - mOutControls, new Bundle()); + mOutControls, mOutBundle); } } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index b828f39a120c..13bea6bd1dd1 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -598,7 +598,6 @@ public final class JobStatus { long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, int internalFlags, int dynamicConstraints) { - this.job = job; this.callingUid = callingUid; this.standbyBucket = standbyBucket; mNamespace = namespace; @@ -626,6 +625,22 @@ public final class JobStatus { this.sourceTag = tag; } + // This needs to be done before setting the field variable. + if (job.getRequiredNetwork() != null) { + // Later, when we check if a given network satisfies the required + // network, we need to know the UID that is requesting it, so push + // the source UID into place. + final JobInfo.Builder builder = new JobInfo.Builder(job); + builder.setRequiredNetwork(new NetworkRequest.Builder(job.getRequiredNetwork()) + .setUids(Collections.singleton(new Range<>(this.sourceUid, this.sourceUid))) + .build()); + // Don't perform validation checks at this point since we've already passed the + // initial validation check. + job = builder.build(false, false); + } + + this.job = job; + final String bnNamespace = namespace == null ? "" : "@" + namespace + "@"; this.batteryName = this.sourceTag != null ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName() @@ -708,21 +723,6 @@ public final class JobStatus { updateNetworkBytesLocked(); - if (job.getRequiredNetwork() != null) { - // Later, when we check if a given network satisfies the required - // network, we need to know the UID that is requesting it, so push - // our source UID into place. - final JobInfo.Builder builder = new JobInfo.Builder(job); - final NetworkRequest.Builder requestBuilder = - new NetworkRequest.Builder(job.getRequiredNetwork()); - requestBuilder.setUids( - Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid))); - builder.setRequiredNetwork(requestBuilder.build()); - // Don't perform validation checks at this point since we've already passed the - // initial validation check. - job = builder.build(false, false); - } - updateMediaBackupExemptionStatus(); } diff --git a/api/Android.bp b/api/Android.bp index d6c14fbdfae3..2b1cfcb82d04 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -404,3 +404,49 @@ build = [ "ApiDocs.bp", "StubLibraries.bp", ] + +genrule_defaults { + name: "flag-api-mapping-generation-defaults", + cmd: "$(location extract-flagged-apis) $(in) $(out)", + tools: ["extract-flagged-apis"], +} + +genrule { + name: "flag-api-mapping-PublicApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-current.txt"], + out: ["flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} + +genrule { + name: "flag-api-mapping-SystemApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-system-current.txt"], + out: ["system_flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} + +genrule { + name: "flag-api-mapping-ModuleLibApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-module-lib-current.txt"], + out: ["module_lib_flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} + +genrule { + name: "flag-api-mapping-SystemServerApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-system-server-current.txt"], + out: ["system_server_flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt index 948e64f22f20..9ffb70496c59 100644 --- a/api/coverage/tools/ExtractFlaggedApis.kt +++ b/api/coverage/tools/ExtractFlaggedApis.kt @@ -25,7 +25,7 @@ fun main(args: Array<String>) { var cb = ApiFile.parseApi(listOf(File(args[0]))) val flagToApi = mutableMapOf<String, MutableList<String>>() cb.getPackages() - .allTopLevelClasses() + .allClasses() .filter { it.methods().size > 0 } .forEach { for (method in it.methods()) { diff --git a/cmds/gpu_counter_producer/Android.bp b/cmds/gpu_counter_producer/Android.bp index 2232345ce4fa..d645d066befd 100644 --- a/cmds/gpu_counter_producer/Android.bp +++ b/cmds/gpu_counter_producer/Android.bp @@ -19,6 +19,4 @@ cc_binary { "-Wunused", "-Wunreachable-code", ], - - soc_specific: true, } diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java new file mode 100644 index 000000000000..b89e2cdbd905 --- /dev/null +++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java @@ -0,0 +1,380 @@ +/* + * Copyright 2023 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.commands.uinput; + +import android.annotation.Nullable; +import android.util.SparseArray; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +import src.com.android.commands.uinput.InputAbsInfo; + +/** + * Parser for the <a href="https://gitlab.freedesktop.org/libevdev/evemu">FreeDesktop evemu</a> + * event recording format. + */ +public class EvemuParser implements EventParser { + private static final String TAG = "UinputEvemuParser"; + + /** + * The device ID to use for all events. Since evemu files only support single-device + * recordings, this will always be the same. + */ + private static final int DEVICE_ID = 1; + private static final int REGISTRATION_DELAY_MILLIS = 500; + + private static class CommentAwareReader { + private final BufferedReader mReader; + private String mNextLine; + + CommentAwareReader(BufferedReader in) throws IOException { + mReader = in; + mNextLine = findNextLine(); + } + + private @Nullable String findNextLine() throws IOException { + String line = ""; + while (line != null && line.length() == 0) { + String unstrippedLine = mReader.readLine(); + if (unstrippedLine == null) { + // End of file. + return null; + } + line = stripComments(unstrippedLine); + } + return line; + } + + private static String stripComments(String line) { + int index = line.indexOf('#'); + // 'N:' lines (which contain the name of the input device) do not support trailing + // comments, to support recording device names that contain #s. + if (index < 0 || line.startsWith("N: ")) { + return line; + } else { + return line.substring(0, index).strip(); + } + } + + /** + * Returns the next line of the file that isn't blank when stripped of comments, or + * {@code null} if the end of the file is reached. However, it does not advance to the + * next line of the file. + */ + public @Nullable String peekLine() { + return mNextLine; + } + + /** Moves to the next line of the file. */ + public void advance() throws IOException { + mNextLine = findNextLine(); + } + + public boolean isAtEndOfFile() { + return mNextLine == null; + } + } + + private final CommentAwareReader mReader; + /** + * The timestamp of the last event returned, of the head of {@link #mQueuedEvents} if there is + * one, or -1 if no events have been returned yet. + */ + private long mLastEventTimeMicros = -1; + private final Queue<Event> mQueuedEvents = new ArrayDeque<>(2); + + public EvemuParser(Reader in) throws IOException { + mReader = new CommentAwareReader(new BufferedReader(in)); + mQueuedEvents.add(parseRegistrationEvent()); + + // The kernel takes a little time to set up an evdev device after the initial + // registration. Any events that we try to inject during this period would be silently + // dropped, so we delay for a short period after registration and before injecting any + // events. + final Event.Builder delayEb = new Event.Builder(); + delayEb.setId(DEVICE_ID); + delayEb.setCommand(Event.Command.DELAY); + delayEb.setDurationMillis(REGISTRATION_DELAY_MILLIS); + mQueuedEvents.add(delayEb.build()); + } + + /** + * Returns the next event in the evemu recording. + */ + public Event getNextEvent() throws IOException { + if (!mQueuedEvents.isEmpty()) { + return mQueuedEvents.remove(); + } + + if (mReader.isAtEndOfFile()) { + return null; + } + + final String[] parts = expectLineWithParts("E", 4); + final String[] timeParts = parts[0].split("\\."); + if (timeParts.length != 2) { + throw new RuntimeException("Invalid timestamp (does not contain a '.')"); + } + // TODO(b/310958309): use timeMicros to set the timestamp on the event being sent. + final long timeMicros = + Long.parseLong(timeParts[0]) * 1_000_000 + Integer.parseInt(timeParts[1]); + final Event.Builder eb = new Event.Builder(); + eb.setId(DEVICE_ID); + eb.setCommand(Event.Command.INJECT); + final int eventType = Integer.parseInt(parts[1], 16); + final int eventCode = Integer.parseInt(parts[2], 16); + final int value = Integer.parseInt(parts[3]); + eb.setInjections(new int[] {eventType, eventCode, value}); + + if (mLastEventTimeMicros == -1) { + // This is the first event being injected, so send it straight away. + mLastEventTimeMicros = timeMicros; + return eb.build(); + } else { + final long delayMicros = timeMicros - mLastEventTimeMicros; + // The shortest delay supported by Handler.sendMessageAtTime (used for timings by the + // Device class) is 1ms, so ignore time differences smaller than that. + if (delayMicros < 1000) { + mLastEventTimeMicros = timeMicros; + return eb.build(); + } else { + // Send a delay now, and queue the actual event for the next call. + mQueuedEvents.add(eb.build()); + mLastEventTimeMicros = timeMicros; + final Event.Builder delayEb = new Event.Builder(); + delayEb.setId(DEVICE_ID); + delayEb.setCommand(Event.Command.DELAY); + delayEb.setDurationMillis((int) (delayMicros / 1000)); + return delayEb.build(); + } + } + } + + private Event parseRegistrationEvent() throws IOException { + // The registration details at the start of a recording are specified by a set of lines + // that have to be in this order: N, I, P, B, A, L, S. Recordings must have exactly one N + // (name) and I (IDs) line. The remaining lines are optional, and there may be multiple + // of those lines. + + final Event.Builder eb = new Event.Builder(); + eb.setId(DEVICE_ID); + eb.setCommand(Event.Command.REGISTER); + eb.setName(expectLine("N")); + + final String[] idStrings = expectLineWithParts("I", 4); + eb.setBusId(Integer.parseInt(idStrings[0], 16)); + eb.setVid(Integer.parseInt(idStrings[1], 16)); + eb.setPid(Integer.parseInt(idStrings[2], 16)); + // TODO(b/302297266): support setting the version ID, and set it to idStrings[3]. + + final SparseArray<int[]> config = new SparseArray<>(); + config.append(Event.UinputControlCode.UI_SET_PROPBIT.getValue(), parseProperties()); + + parseAxisBitmaps(config); + + eb.setConfiguration(config); + if (config.contains(Event.UinputControlCode.UI_SET_FFBIT.getValue())) { + // If the device specifies any force feedback effects, the kernel will require the + // ff_effects_max value to be set. + eb.setFfEffectsMax(config.get(Event.UinputControlCode.UI_SET_FFBIT.getValue()).length); + } + + eb.setAbsInfo(parseAbsInfos()); + + // L: and S: lines allow the initial states of the device's LEDs and switches to be + // recorded. However, the FreeDesktop implementation doesn't support actually setting these + // states at the start of playback (apparently due to concerns over race conditions), and we + // have no need for this feature either, so for now just skip over them. + skipUnsupportedLines("L"); + skipUnsupportedLines("S"); + + return eb.build(); + } + + private int[] parseProperties() throws IOException { + final List<String> propBitmapParts = new ArrayList<>(); + String line = acceptLine("P"); + while (line != null) { + propBitmapParts.addAll(List.of(line.strip().split(" "))); + line = acceptLine("P"); + } + return hexStringBitmapToEventCodes(propBitmapParts); + } + + private void parseAxisBitmaps(SparseArray<int[]> config) throws IOException { + final Map<Integer, List<String>> axisBitmapParts = new HashMap<>(); + String line = acceptLine("B"); + while (line != null) { + final String[] parts = line.strip().split(" "); + if (parts.length < 2) { + throw new RuntimeException( + "Expected event type and at least one bitmap byte on 'B:' line; only found " + + parts.length + " elements"); + } + final int eventType = Integer.parseInt(parts[0], 16); + // EV_SYN cannot be configured through uinput, so skip it. + if (eventType != Event.EV_SYN) { + if (!axisBitmapParts.containsKey(eventType)) { + axisBitmapParts.put(eventType, new ArrayList<>()); + } + for (int i = 1; i < parts.length; i++) { + axisBitmapParts.get(eventType).add(parts[i]); + } + } + line = acceptLine("B"); + } + final List<Integer> eventTypesToSet = new ArrayList<>(); + for (var entry : axisBitmapParts.entrySet()) { + if (entry.getValue().size() == 0) { + continue; + } + final Event.UinputControlCode controlCode = + Event.UinputControlCode.forEventType(entry.getKey()); + final int[] eventCodes = hexStringBitmapToEventCodes(entry.getValue()); + if (controlCode != null && eventCodes.length > 0) { + config.append(controlCode.getValue(), eventCodes); + eventTypesToSet.add(entry.getKey()); + } + } + config.append( + Event.UinputControlCode.UI_SET_EVBIT.getValue(), unboxIntList(eventTypesToSet)); + } + + private SparseArray<InputAbsInfo> parseAbsInfos() throws IOException { + final SparseArray<InputAbsInfo> absInfos = new SparseArray<>(); + String line = acceptLine("A"); + while (line != null) { + final String[] parts = line.strip().split(" "); + if (parts.length < 5 || parts.length > 6) { + throw new RuntimeException( + "'A:' lines should have the format 'A: <index (hex)> <min> <max> <fuzz> " + + "<flat> [<resolution>]'; expected 5 or 6 numbers but found " + + parts.length); + } + final int axisCode = Integer.parseInt(parts[0], 16); + final InputAbsInfo info = new InputAbsInfo(); + info.minimum = Integer.parseInt(parts[1]); + info.maximum = Integer.parseInt(parts[2]); + info.fuzz = Integer.parseInt(parts[3]); + info.flat = Integer.parseInt(parts[4]); + info.resolution = parts.length > 5 ? Integer.parseInt(parts[5]) : 0; + absInfos.append(axisCode, info); + line = acceptLine("A"); + } + return absInfos; + } + + private void skipUnsupportedLines(String type) throws IOException { + if (acceptLine(type) != null) { + while (acceptLine(type) != null) { + // Skip the line. + } + } + } + + /** + * Returns the contents of the next line in the file if it has the given type, or raises an + * error if it does not. + * + * @param type the type of the line to expect, represented by the letter before the ':'. + * @return the part of the line after the ": ". + */ + private String expectLine(String type) throws IOException { + final String line = acceptLine(type); + if (line == null) { + throw new RuntimeException("Expected line of type '" + type + "'"); + } else { + return line; + } + } + + /** + * Peeks at the next line in the file to see if it has the given type, and if so, returns its + * contents and advances the reader. + * + * @param type the type of the line to accept, represented by the letter before the ':'. + * @return the part of the line after the ": ", if the type matches; otherwise {@code null}. + */ + private @Nullable String acceptLine(String type) throws IOException { + final String line = mReader.peekLine(); + if (line == null) { + return null; + } + final String[] lineParts = line.split(": ", 2); + if (lineParts.length < 2) { + // TODO(b/302297266): make a proper exception class for syntax errors, including line + // numbers, etc.. (We can use LineNumberReader to track them.) + throw new RuntimeException("Line without ': '"); + } + if (lineParts[0].equals(type)) { + mReader.advance(); + return lineParts[1]; + } else { + return null; + } + } + + /** + * Like {@link #expectLine(String)}, but also checks that the contents of the line is formed of + * {@code numParts} space-separated parts. + * + * @param type the type of the line to expect, represented by the letter before the ':'. + * @param numParts the number of parts to expect. + * @return the part of the line after the ": ", split into {@code numParts} sections. + */ + private String[] expectLineWithParts(String type, int numParts) throws IOException { + final String[] parts = expectLine(type).strip().split(" "); + if (parts.length != numParts) { + throw new RuntimeException("Expected a '" + type + "' line with " + numParts + + " parts, found one with " + parts.length); + } + return parts; + } + + private static int[] hexStringBitmapToEventCodes(List<String> strs) { + final List<Integer> codes = new ArrayList<>(); + for (int iByte = 0; iByte < strs.size(); iByte++) { + int b = Integer.parseInt(strs.get(iByte), 16); + if (b < 0x0 || b > 0xff) { + throw new RuntimeException("Bitmap part '" + strs.get(iByte) + + "' invalid; parts must be between 00 and ff."); + } + for (int iBit = 0; iBit < 8; iBit++) { + if ((b & 1) != 0) { + codes.add(iByte * 8 + iBit); + } + b >>= 1; + } + } + return unboxIntList(codes); + } + + private static int[] unboxIntList(List<Integer> list) { + final int[] array = new int[list.size()]; + Arrays.setAll(array, list::get); + return array; + } +} diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java index 4498bc2a09d6..5ec40e5d04e3 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Event.java +++ b/cmds/uinput/src/com/android/commands/uinput/Event.java @@ -16,6 +16,7 @@ package com.android.commands.uinput; +import android.annotation.Nullable; import android.util.SparseArray; import java.util.Arrays; @@ -39,6 +40,7 @@ public class Event { // Constants representing evdev event types, from include/uapi/linux/input-event-codes.h in the // kernel. + public static final int EV_SYN = 0x00; public static final int EV_KEY = 0x01; public static final int EV_REL = 0x02; public static final int EV_ABS = 0x03; @@ -69,19 +71,23 @@ public class Event { public int getValue() { return mValue; } - } - - // These constants come from "include/uapi/linux/input.h" in the kernel - enum Bus { - USB(0x03), BLUETOOTH(0x05); - private final int mValue; - - Bus(int value) { - mValue = value; - } - int getValue() { - return mValue; + /** + * Returns the control code for the given evdev event type, or {@code null} if there is no + * control code for that type. + */ + public static @Nullable UinputControlCode forEventType(int eventType) { + return switch (eventType) { + case EV_KEY -> UI_SET_KEYBIT; + case EV_REL -> UI_SET_RELBIT; + case EV_ABS -> UI_SET_ABSBIT; + case EV_MSC -> UI_SET_MSCBIT; + case EV_SW -> UI_SET_SWBIT; + case EV_LED -> UI_SET_LEDBIT; + case EV_SND -> UI_SET_SNDBIT; + case EV_FF -> UI_SET_FFBIT; + default -> null; + }; } } @@ -90,7 +96,7 @@ public class Event { private String mName; private int mVid; private int mPid; - private Bus mBus; + private int mBusId; private int[] mInjections; private SparseArray<int[]> mConfiguration; private int mDurationMillis; @@ -120,7 +126,7 @@ public class Event { } public int getBus() { - return mBus.getValue(); + return mBusId; } public int[] getInjections() { @@ -168,7 +174,7 @@ public class Event { + ", name=" + mName + ", vid=" + mVid + ", pid=" + mPid - + ", bus=" + mBus + + ", busId=" + mBusId + ", events=" + Arrays.toString(mInjections) + ", configuration=" + mConfiguration + ", duration=" + mDurationMillis + "ms" @@ -218,8 +224,8 @@ public class Event { mEvent.mPid = pid; } - public void setBus(Bus bus) { - mEvent.mBus = bus; + public void setBusId(int busId) { + mEvent.mBusId = busId; } public void setDurationMillis(int durationMillis) { diff --git a/cmds/uinput/src/com/android/commands/uinput/EventParser.java b/cmds/uinput/src/com/android/commands/uinput/EventParser.java new file mode 100644 index 000000000000..a4df03df7cd1 --- /dev/null +++ b/cmds/uinput/src/com/android/commands/uinput/EventParser.java @@ -0,0 +1,29 @@ +/* + * Copyright 2023 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.commands.uinput; + +import java.io.IOException; + +/** + * Interface for a class that reads a stream of {@link Event}s. + */ +public interface EventParser { + /** + * Returns the next event in the file that the parser is reading from. + */ + Event getNextEvent() throws IOException; +} diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java index a2195c7809be..888ec5a1d33a 100644 --- a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java +++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java @@ -22,7 +22,7 @@ import android.util.Log; import android.util.SparseArray; import java.io.IOException; -import java.io.InputStreamReader; +import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -34,12 +34,12 @@ import src.com.android.commands.uinput.InputAbsInfo; /** * A class that parses the JSON-like event format described in the README to build {@link Event}s. */ -public class JsonStyleParser { +public class JsonStyleParser implements EventParser { private static final String TAG = "UinputJsonStyleParser"; private JsonReader mReader; - public JsonStyleParser(InputStreamReader in) { + public JsonStyleParser(Reader in) { mReader = new JsonReader(in); mReader.setLenient(true); } @@ -62,7 +62,7 @@ public class JsonStyleParser { case "name" -> eb.setName(mReader.nextString()); case "vid" -> eb.setVid(readInt()); case "pid" -> eb.setPid(readInt()); - case "bus" -> eb.setBus(readBus()); + case "bus" -> eb.setBusId(readBus()); case "events" -> { int[] injections = readInjectedEvents().stream() .mapToInt(Integer::intValue).toArray(); @@ -139,9 +139,35 @@ public class JsonStyleParser { }); } - private Event.Bus readBus() throws IOException { + private int readBus() throws IOException { String val = mReader.nextString(); - return Event.Bus.valueOf(val.toUpperCase()); + // See include/uapi/linux/input.h in the kernel for the source of these constants. + return switch (val.toUpperCase()) { + case "PCI" -> 0x01; + case "ISAPNP" -> 0x02; + case "USB" -> 0x03; + case "HIL" -> 0x04; + case "BLUETOOTH" -> 0x05; + case "VIRTUAL" -> 0x06; + case "ISA" -> 0x10; + case "I8042" -> 0x11; + case "XTKBD" -> 0x12; + case "RS232" -> 0x13; + case "GAMEPORT" -> 0x14; + case "PARPORT" -> 0x15; + case "AMIGA" -> 0x16; + case "ADB" -> 0x17; + case "I2C" -> 0x18; + case "HOST" -> 0x19; + case "GSC" -> 0x1A; + case "ATARI" -> 0x1B; + case "SPI" -> 0x1C; + case "RMI" -> 0x1D; + case "CEC" -> 0x1E; + case "INTEL_ISHTP" -> 0x1F; + case "AMD_SFH" -> 0x20; + default -> throw new IllegalArgumentException("Invalid bus ID " + val); + }; } private SparseArray<int[]> readConfiguration() diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java index fe76abb9861d..684a12fc8f37 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java +++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java @@ -19,12 +19,12 @@ package com.android.commands.uinput; import android.util.Log; import android.util.SparseArray; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.util.Objects; /** @@ -35,7 +35,7 @@ import java.util.Objects; public class Uinput { private static final String TAG = "UINPUT"; - private final JsonStyleParser mParser; + private final EventParser mParser; private final SparseArray<Device> mDevices; private static void usage() { @@ -74,12 +74,32 @@ public class Uinput { private Uinput(InputStream in) { mDevices = new SparseArray<Device>(); try { - mParser = new JsonStyleParser(new InputStreamReader(in, "UTF-8")); - } catch (UnsupportedEncodingException e) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); + mParser = isEvemuFile(reader) ? new EvemuParser(reader) : new JsonStyleParser(reader); + } catch (IOException e) { throw new RuntimeException(e); } } + private boolean isEvemuFile(BufferedReader in) throws IOException { + // After zero or more empty lines (not even containing horizontal whitespace), evemu + // recordings must either start with '#' (indicating the EVEMU version header or a comment) + // or 'N' (for the name line). If we encounter anything else, assume it's a JSON-style input + // file. + + String lineSep = System.lineSeparator(); + char[] buf = new char[1]; + + in.mark(1 /* readAheadLimit */); + int charsRead = in.read(buf); + while (charsRead > 0 && lineSep.contains(String.valueOf(buf[0]))) { + in.mark(1 /* readAheadLimit */); + charsRead = in.read(buf); + } + in.reset(); + return buf[0] == '#' || buf[0] == 'N'; + } + private void run() { try { Event e = null; diff --git a/core/api/current.txt b/core/api/current.txt index 207abb216c37..812d4cd67ab7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12781,6 +12781,7 @@ package android.content.pm { method public boolean isPackageSuspended(); method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String); method public abstract boolean isSafeMode(); + method @FlaggedApi("android.content.pm.get_package_info") @WorkerThread public <T> T parseAndroidManifest(@NonNull String, @NonNull java.util.function.Function<android.content.res.XmlResourceParser,T>) throws java.io.IOException; method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String); method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String); method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int); @@ -36759,6 +36760,7 @@ package android.provider { field public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "android.settings.APP_OPEN_BY_DEFAULT_SETTINGS"; field public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS"; field public static final String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS"; + field @FlaggedApi("android.app.modes_api") public static final String ACTION_AUTOMATIC_ZEN_RULE_SETTINGS = "android.settings.AUTOMATIC_ZEN_RULE_SETTINGS"; field public static final String ACTION_AUTO_ROTATE_SETTINGS = "android.settings.AUTO_ROTATE_SETTINGS"; field public static final String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS"; field public static final String ACTION_BIOMETRIC_ENROLL = "android.settings.BIOMETRIC_ENROLL"; @@ -36846,6 +36848,7 @@ package android.provider { field public static final String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled"; field public static final String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE"; field public static final String EXTRA_AUTHORITIES = "authorities"; + field @FlaggedApi("android.app.modes_api") public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID = "android.provider.extra.AUTOMATIC_ZEN_RULE_ID"; field public static final String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled"; field public static final String EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED = "android.provider.extra.BIOMETRIC_AUTHENTICATORS_ALLOWED"; field public static final String EXTRA_CHANNEL_FILTER_LIST = "android.provider.extra.CHANNEL_FILTER_LIST"; @@ -45159,6 +45162,7 @@ package android.telephony { method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid); method public boolean canManageSubscription(android.telephony.SubscriptionInfo); + method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context); method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); @@ -54183,7 +54187,7 @@ package android.view.accessibility { method public boolean isEnabled(); method public boolean isFocusable(); method public boolean isFocused(); - method public boolean isGranularScrollingSupported(); + method @FlaggedApi("android.view.accessibility.granular_scrolling") public boolean isGranularScrollingSupported(); method public boolean isHeading(); method public boolean isImportantForAccessibility(); method public boolean isLongClickable(); @@ -54233,7 +54237,7 @@ package android.view.accessibility { method public void setError(CharSequence); method public void setFocusable(boolean); method public void setFocused(boolean); - method public void setGranularScrollingSupported(boolean); + method @FlaggedApi("android.view.accessibility.granular_scrolling") public void setGranularScrollingSupported(boolean); method public void setHeading(boolean); method public void setHintText(CharSequence); method public void setImportantForAccessibility(boolean); @@ -54288,7 +54292,7 @@ package android.view.accessibility { field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT"; field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE"; field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT"; - field public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT"; + field @FlaggedApi("android.view.accessibility.granular_scrolling") public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT"; field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT"; field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT"; field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; @@ -54391,10 +54395,9 @@ package android.view.accessibility { public static final class AccessibilityNodeInfo.CollectionInfo { ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean); ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean, int); - ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean, int, int, int); method public int getColumnCount(); - method public int getImportantForAccessibilityItemCount(); - method public int getItemCount(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") public int getImportantForAccessibilityItemCount(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") public int getItemCount(); method public int getRowCount(); method public int getSelectionMode(); method public boolean isHierarchical(); @@ -54403,18 +54406,18 @@ package android.view.accessibility { field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2 field public static final int SELECTION_MODE_NONE = 0; // 0x0 field public static final int SELECTION_MODE_SINGLE = 1; // 0x1 - field public static final int UNDEFINED = -1; // 0xffffffff - } - - public static final class AccessibilityNodeInfo.CollectionInfo.Builder { - ctor public AccessibilityNodeInfo.CollectionInfo.Builder(); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo build(); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setColumnCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setHierarchical(boolean); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setImportantForAccessibilityItemCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setItemCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setRowCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setSelectionMode(int); + field @FlaggedApi("android.view.accessibility.collection_info_item_counts") public static final int UNDEFINED = -1; // 0xffffffff + } + + @FlaggedApi("android.view.accessibility.collection_info_item_counts") public static final class AccessibilityNodeInfo.CollectionInfo.Builder { + ctor @FlaggedApi("android.view.accessibility.collection_info_item_counts") public AccessibilityNodeInfo.CollectionInfo.Builder(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo build(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setColumnCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setHierarchical(boolean); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setImportantForAccessibilityItemCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setItemCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setRowCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setSelectionMode(int); } public static final class AccessibilityNodeInfo.CollectionItemInfo { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 275fe7790e84..c282e4b6f3eb 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -307,7 +307,7 @@ package android { field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE"; field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER"; field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER"; - field public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE"; + field @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE"; field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION"; field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM"; field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER"; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index bb335fae887b..6c10f495e7bf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIO import static android.Manifest.permission.DETECT_SCREEN_CAPTURE; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.inMultiWindowMode; import static android.os.Process.myUid; @@ -7603,15 +7604,17 @@ public class Activity extends ContextThemeWrapper * @param taskDescription The TaskDescription properties that describe the task with this activity */ public void setTaskDescription(ActivityManager.TaskDescription taskDescription) { - if (mTaskDescription != taskDescription) { - mTaskDescription.copyFromPreserveHiddenFields(taskDescription); - // Scale the icon down to something reasonable if it is provided - if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) { - final int size = ActivityManager.getLauncherLargeIconSizeInner(this); - final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size, - true); - mTaskDescription.setIcon(Icon.createWithBitmap(icon)); - } + if (taskDescription == null || mTaskDescription.equals(taskDescription)) { + return; + } + + mTaskDescription.copyFromPreserveHiddenFields(taskDescription); + // Scale the icon down to something reasonable if it is provided + if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) { + final int size = ActivityManager.getLauncherLargeIconSizeInner(this); + final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size, + true); + mTaskDescription.setIcon(Icon.createWithBitmap(icon)); } ActivityClient.getInstance().setTaskDescription(mToken, mTaskDescription); } @@ -9439,6 +9442,15 @@ public class Activity extends ContextThemeWrapper ActivityClient.getInstance().enableTaskLocaleOverride(mToken); } + /** + * Request ActivityRecordInputSink to enable or disable blocking input events. + * @hide + */ + @RequiresPermission(INTERNAL_SYSTEM_WINDOW) + public void setActivityRecordInputSinkEnabled(boolean enabled) { + ActivityClient.getInstance().setActivityRecordInputSinkEnabled(mToken, enabled); + } + class HostCallbacks extends FragmentHostCallback<Activity> { public HostCallbacks() { super(Activity.this /*activity*/); diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index b35e87b541d3..b8bd030872c1 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -16,6 +16,8 @@ package android.app; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; + import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.content.ComponentName; @@ -614,6 +616,15 @@ public class ActivityClient { } } + @RequiresPermission(INTERNAL_SYSTEM_WINDOW) + void setActivityRecordInputSinkEnabled(IBinder activityToken, boolean enabled) { + try { + getActivityClientController().setActivityRecordInputSinkEnabled(activityToken, enabled); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + /** * Shows or hides a Camera app compat toggle for stretched issues with the requested state. * diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 02eaf0b3bbd3..8af12161cc08 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -232,6 +232,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import com.android.org.conscrypt.TrustedCertificateStore; import com.android.server.am.MemInfoDumpProto; +import com.android.window.flags.Flags; import dalvik.annotation.optimization.NeverCompile; import dalvik.system.AppSpecializationHooks; @@ -3713,7 +3714,13 @@ public final class ActivityThread extends ClientTransactionHandler final ArrayList<ResultInfo> list = new ArrayList<>(); list.add(new ResultInfo(id, requestCode, resultCode, data)); final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread); - clientTransaction.addCallback(ActivityResultItem.obtain(activityToken, list)); + final ActivityResultItem activityResultItem = ActivityResultItem.obtain( + activityToken, list); + if (Flags.bundleClientTransactionFlag()) { + clientTransaction.addTransactionItem(activityResultItem); + } else { + clientTransaction.addCallback(activityResultItem); + } try { mAppThread.scheduleTransaction(clientTransaction); } catch (RemoteException e) { @@ -4492,16 +4499,26 @@ public final class ActivityThread extends ClientTransactionHandler private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) { final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); - transaction.setLifecycleStateRequest(PauseActivityItem.obtain(r.token, + final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token, r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags, - /* dontReport */ false, /* autoEnteringPip */ false)); + /* dontReport */ false, /* autoEnteringPip */ false); + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(pauseActivityItem); + } else { + transaction.setLifecycleStateRequest(pauseActivityItem); + } executeTransaction(transaction); } private void scheduleResume(ActivityClientRecord r) { final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); - transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(r.token, - /* isForward */ false, /* shouldSendCompatFakeFocus */ false)); + final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token, + /* isForward */ false, /* shouldSendCompatFakeFocus */ false); + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(resumeActivityItem); + } else { + transaction.setLifecycleStateRequest(resumeActivityItem); + } executeTransaction(transaction); } @@ -6092,8 +6109,13 @@ public final class ActivityThread extends ClientTransactionHandler TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); // Schedule the transaction. final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); - transaction.addCallback(activityRelaunchItem); - transaction.setLifecycleStateRequest(lifecycleRequest); + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(activityRelaunchItem); + transaction.addTransactionItem(lifecycleRequest); + } else { + transaction.addCallback(activityRelaunchItem); + transaction.setLifecycleStateRequest(lifecycleRequest); + } executeTransaction(transaction); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index ca6d8df1df12..a4c3bb824502 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -80,6 +80,8 @@ import android.content.pm.SuspendDialogInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; import android.content.pm.dex.ArtManager; +import android.content.pm.parsing.ApkLiteParseUtils; +import android.content.res.ApkAssets; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; @@ -144,6 +146,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; +import java.util.function.Function; /** @hide */ public class ApplicationPackageManager extends PackageManager { @@ -4024,4 +4027,34 @@ public class ApplicationPackageManager extends PackageManager { throw e.rethrowFromSystemServer(); } } + + @Override + public <T> T parseAndroidManifest(@NonNull String apkFilePath, + @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException { + Objects.requireNonNull(apkFilePath, "apkFilePath cannot be null"); + Objects.requireNonNull(parserFunction, "parserFunction cannot be null"); + try (XmlResourceParser xmlResourceParser = getAndroidManifestParser(apkFilePath)) { + return parserFunction.apply(xmlResourceParser); + } catch (IOException e) { + Log.w(TAG, "Failed to get the android manifest parser", e); + throw e; + } + } + + private static XmlResourceParser getAndroidManifestParser(@NonNull String apkFilePath) + throws IOException { + ApkAssets apkAssets = null; + try { + apkAssets = ApkAssets.loadFromPath(apkFilePath); + return apkAssets.openXml(ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME); + } finally { + if (apkAssets != null) { + try { + apkAssets.close(); + } catch (Throwable ignored) { + Log.w(TAG, "Failed to close apkAssets", ignored); + } + } + } + } } diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index a3c5e1c67e1b..7370fc36c23e 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -191,4 +191,14 @@ interface IActivityClientController { */ boolean isRequestedToLaunchInTaskFragment(in IBinder activityToken, in IBinder taskFragmentToken); + + /** + * Enable or disable ActivityRecordInputSink to block input events. + * + * @param token The token for the activity that requests to toggle. + * @param enabled Whether the input evens are blocked by ActivityRecordInputSink. + */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.INTERNAL_SYSTEM_WINDOW)") + oneway void setActivityRecordInputSinkEnabled(in IBinder activityToken, boolean enabled); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 56d0d1f2843d..6c9c14fbc83a 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -51,6 +51,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.UserHandle; +import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.Adjustment; import android.service.notification.Condition; @@ -1253,7 +1254,8 @@ public class NotificationManager { * <p> * If this method returns true, calls to * {@link #updateAutomaticZenRule(String, AutomaticZenRule)} may fail and apps should defer - * rule management to system settings/uis. + * rule management to system settings/uis via + * {@link Settings#ACTION_AUTOMATIC_ZEN_RULE_SETTINGS}. */ @FlaggedApi(Flags.FLAG_MODES_API) public boolean areAutomaticZenRulesUserManaged() { diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index cc56a1cee7a2..d8448dcb4f9c 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -72,6 +72,7 @@ per-file *Notification* = file:/packages/SystemUI/OWNERS per-file *Zen* = file:/packages/SystemUI/OWNERS per-file *StatusBar* = file:/packages/SystemUI/OWNERS per-file *UiModeManager* = file:/packages/SystemUI/OWNERS +per-file notification.aconfig = file:/packages/SystemUI/OWNERS # PackageManager per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index bbd07b8c21ea..35ce10223aa6 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -48,3 +48,10 @@ flag { description: "Update DumpSys to include information about migrated APIs in DPE" bug: "304999634" } + +flag { + name: "quiet_mode_credential_bug_fix" + namespace: "enterprise" + description: "Guards a bugfix that ends the credential input flow if the managed user has not stopped." + bug: "293441361" +} diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 7c34cdefe95f..5e5526802fe9 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -75,7 +75,6 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { /** * Adds a message to the end of the sequence of transaction items. * @param item A single message that can contain a client activity/window request/callback. - * TODO(b/260873529): replace both {@link #addCallback} and {@link #setLifecycleStateRequest}. */ public void addTransactionItem(@NonNull ClientTransactionItem item) { if (mTransactionItems == null) { diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index dfbccb41d045..475c6fb9a48a 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -235,7 +235,9 @@ public class TransactionExecutorHelper { * Configuration - ActivityResult - Configuration - ActivityResult * index 1 will be returned, because ActivityResult request on position 1 will be the last * request that moves activity to the RESUMED state where it will eventually end. + * @deprecated to be removed with {@link TransactionExecutor#executeCallbacks}. */ + @Deprecated static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null || callbacks.isEmpty() diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING index 49a4467b1f67..47a152aa6cca 100644 --- a/core/java/android/app/time/TEST_MAPPING +++ b/core/java/android/app/time/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.app." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "FrameworksServicesTests", "options": [ diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING index c050a55a3e18..9517fb99b04a 100644 --- a/core/java/android/app/timedetector/TEST_MAPPING +++ b/core/java/android/app/timedetector/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.app." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "FrameworksServicesTests", "options": [ diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING index 46656d125b70..fd41b869efaf 100644 --- a/core/java/android/app/timezonedetector/TEST_MAPPING +++ b/core/java/android/app/timezonedetector/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.app." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "FrameworksServicesTests", "options": [ diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java index 016d97f60e26..aa3239237478 100644 --- a/core/java/android/app/usage/ParcelableUsageEventList.java +++ b/core/java/android/app/usage/ParcelableUsageEventList.java @@ -48,13 +48,16 @@ public final class ParcelableUsageEventList implements Parcelable { private List<Event> mList; - public ParcelableUsageEventList(List<Event> list) { + public ParcelableUsageEventList(@NonNull List<Event> list) { + if (list == null) { + throw new IllegalArgumentException("Empty list"); + } mList = list; } private ParcelableUsageEventList(Parcel in) { final int N = in.readInt(); - mList = new ArrayList<>(); + mList = new ArrayList<>(N); if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); if (N <= 0) { return; diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 1eb452cfd085..6c7eba06dea7 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -24,9 +24,11 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -35,6 +37,7 @@ import java.util.List; * from which to read {@link android.app.usage.UsageEvents.Event} objects. */ public final class UsageEvents implements Parcelable { + private static final String TAG = "UsageEvents"; /** @hide */ public static final String INSTANT_APP_PACKAGE_NAME = "android.instant_app"; @@ -786,10 +789,20 @@ public final class UsageEvents implements Parcelable { } private void readUsageEventsFromParcelWithParceledList(Parcel in) { + mEventCount = in.readInt(); mIndex = in.readInt(); - mEventsToWrite = in.readParcelable(UsageEvents.class.getClassLoader(), - ParcelableUsageEventList.class).getList(); - mEventCount = mEventsToWrite.size(); + ParcelableUsageEventList slice = in.readParcelable(getClass().getClassLoader(), + ParcelableUsageEventList.class); + if (slice != null) { + mEventsToWrite = slice.getList(); + } else { + mEventsToWrite = new ArrayList<>(); + } + + if (mEventCount != mEventsToWrite.size()) { + Log.w(TAG, "Partial usage event list received: " + mEventCount + " != " + + mEventsToWrite.size()); + } } private void readUsageEventsFromParcelWithBlob(Parcel in) { @@ -1065,6 +1078,7 @@ public final class UsageEvents implements Parcelable { } private void writeUsageEventsToParcelWithParceledList(Parcel dest, int flags) { + dest.writeInt(mEventCount); dest.writeInt(mIndex); dest.writeParcelable(new ParcelableUsageEventList(mEventsToWrite), flags); } diff --git a/core/java/android/companion/utils/FeatureUtils.java b/core/java/android/companion/utils/FeatureUtils.java deleted file mode 100644 index a382e09ae7b2..000000000000 --- a/core/java/android/companion/utils/FeatureUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 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.companion.utils; - -import android.os.Binder; -import android.os.Build; -import android.provider.DeviceConfig; - -/** - * Util class for feature flags - * - * @hide - */ -public final class FeatureUtils { - - private static final String NAMESPACE_COMPANION = "companion"; - - private static final String PROPERTY_PERM_SYNC_ENABLED = "perm_sync_enabled"; - - public static boolean isPermSyncEnabled() { - // Permissions sync is always enabled in debuggable mode. - if (Build.isDebuggable()) { - return true; - } - - // Clear app identity to read the device config for feature flag. - final long identity = Binder.clearCallingIdentity(); - try { - return DeviceConfig.getBoolean(NAMESPACE_COMPANION, - PROPERTY_PERM_SYNC_ENABLED, false); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private FeatureUtils() { - } -} diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c3b3423c1a57..fe31c9dbf27d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -34,6 +34,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.annotation.WorkerThread; import android.annotation.XmlRes; import android.app.ActivityManager; import android.app.ActivityThread; @@ -96,6 +97,7 @@ import com.android.internal.util.DataClass; import dalvik.system.VMRuntime; import java.io.File; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.cert.Certificate; @@ -108,6 +110,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; +import java.util.function.Function; /** * Class for retrieving various kinds of information related to the application @@ -11426,4 +11429,60 @@ public abstract class PackageManager { throw new UnsupportedOperationException( "unregisterPackageMonitorCallback not implemented in subclass"); } + + /** + * Retrieve AndroidManifest.xml information for the given application apk path. + * + * <p>Example: + * + * <pre><code> + * Bundle result; + * try { + * result = getContext().getPackageManager().parseAndroidManifest(apkFilePath, + * xmlResourceParser -> { + * Bundle bundle = new Bundle(); + * // Search the start tag + * int type; + * while ((type = xmlResourceParser.next()) != XmlPullParser.START_TAG + * && type != XmlPullParser.END_DOCUMENT) { + * } + * if (type != XmlPullParser.START_TAG) { + * return bundle; + * } + * + * // Start to read the tags and attributes from the xmlResourceParser + * if (!xmlResourceParser.getName().equals("manifest")) { + * return bundle; + * } + * String packageName = xmlResourceParser.getAttributeValue(null, "package"); + * bundle.putString("package", packageName); + * + * // Continue to read the tags and attributes from the xmlResourceParser + * + * return bundle; + * }); + * } catch (IOException e) { + * } + * </code></pre> + * + * Note: When the parserFunction is invoked, the client can read the AndroidManifest.xml + * information by the XmlResourceParser object. After leaving the parserFunction, the + * XmlResourceParser object will be closed. + * + * @param apkFilePath The path of an application apk file. + * @param parserFunction The parserFunction will be invoked with the XmlResourceParser object + * after getting the AndroidManifest.xml of an application package. + * + * @return Returns the result of the {@link Function#apply(Object)}. + * + * @throws IOException if the AndroidManifest.xml of an application package cannot be + * read or accessed. + */ + @FlaggedApi(android.content.pm.Flags.FLAG_GET_PACKAGE_INFO) + @WorkerThread + public <T> T parseAndroidManifest(@NonNull String apkFilePath, + @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException { + throw new UnsupportedOperationException( + "parseAndroidManifest not implemented in subclass"); + } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 89f889f988e2..fe95a2ab8e6d 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1414,7 +1414,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * otherwise the value will be equal to 1. * Note that this level is just a number of supported levels (the granularity of control). * There is no actual physical power units tied to this level.</p> - * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p>This key is available on all devices.</p> * * @see CaptureRequest#FLASH_MODE */ @@ -1430,7 +1430,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * or equal to <code>android.flash.info.singleStrengthMaxLevel</code>. * Note for devices that do not support the manual flash strength control * feature, this level will always be equal to 1.</p> - * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p>This key is available on all devices.</p> */ @PublicKey @NonNull @@ -1450,7 +1450,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * android.flash.info.singleStrengthMaxLevel i.e. the ratio of * android.flash.info.torchStrengthMaxLevel:android.flash.info.singleStrengthMaxLevel * is not guaranteed to be the ratio of actual brightness.</p> - * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p>This key is available on all devices.</p> * * @see CaptureRequest#FLASH_MODE */ @@ -1466,7 +1466,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * or equal to android.flash.info.torchStrengthMaxLevel. * Note for the devices that do not support the manual flash strength control feature, * this level will always be equal to 1.</p> - * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p>This key is available on all devices.</p> */ @PublicKey @NonNull diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 5d0697806512..93cae545deab 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -2688,7 +2688,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * set to TORCH; * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is * set to SINGLE</p> - * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p>This key is available on all devices.</p> * * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#FLASH_MODE diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 0d204f3ececa..12ab0f6e50e1 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2974,7 +2974,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * set to TORCH; * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is * set to SINGLE</p> - * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p>This key is available on all devices.</p> * * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#FLASH_MODE diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java index 7756b9ca7e5a..c3791888b44f 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -22,9 +22,11 @@ import android.annotation.RequiresPermission; import android.content.Context; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Trace; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; @@ -45,6 +47,8 @@ import java.util.concurrent.Executor; @VisibleForTesting(visibility = Visibility.PACKAGE) public final class DeviceStateManagerGlobal { private static DeviceStateManagerGlobal sInstance; + private static final String TAG = "DeviceStateManagerGlobal"; + private static final boolean DEBUG = Build.IS_DEBUGGABLE; /** * Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a @@ -400,11 +404,29 @@ public final class DeviceStateManagerGlobal { } void notifyBaseStateChanged(int newBaseState) { - mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState)); + execute("notifyBaseStateChanged", + () -> mDeviceStateCallback.onBaseStateChanged(newBaseState)); } void notifyStateChanged(int newDeviceState) { - mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState)); + execute("notifyStateChanged", + () -> mDeviceStateCallback.onStateChanged(newDeviceState)); + } + + private void execute(String traceName, Runnable r) { + mExecutor.execute(() -> { + if (DEBUG) { + Trace.beginSection( + mDeviceStateCallback.getClass().getSimpleName() + "#" + traceName); + } + try { + r.run(); + } finally { + if (DEBUG) { + Trace.endSection(); + } + } + }); } } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 4791a8341912..f71e853a1170 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -721,8 +721,19 @@ public abstract class DisplayManagerInternal { public interface DisplayOffloadSession { /** Provide the display state to use in place of state DOZE. */ void setDozeStateOverride(int displayState); - /** Returns the associated DisplayOffloader. */ - DisplayOffloader getDisplayOffloader(); + + /** Whether the session is active. */ + boolean isActive(); + + /** + * Update the brightness from the offload chip. + * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and + * {@link PowerManager.BRIGHTNESS_MAX}, or + * {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} which removes + * the brightness from offload. Other values will be ignored. + */ + void updateBrightness(float brightness); + /** Returns whether displayoffload supports the given display state. */ static boolean isSupportedOffloadState(int displayState) { return Display.isSuspendedState(displayState); diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index f817fb8dcaed..1100731702a2 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -82,8 +82,8 @@ public final class BinderProxy implements IBinder { private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE; private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1; /** - * Debuggable builds will throw an BinderProxyMapSizeException if the number of - * map entries exceeds: + * We will throw a BinderProxyMapSizeException if the number of map entries + * exceeds: */ private static final int CRASH_AT_SIZE = 25_000; diff --git a/core/java/android/permission/IOnPermissionsChangeListener.aidl b/core/java/android/permission/IOnPermissionsChangeListener.aidl index afacf1a74ca6..c68c0c9408a7 100644 --- a/core/java/android/permission/IOnPermissionsChangeListener.aidl +++ b/core/java/android/permission/IOnPermissionsChangeListener.aidl @@ -21,5 +21,5 @@ package android.permission; * {@hide} */ oneway interface IOnPermissionsChangeListener { - void onPermissionsChanged(int uid, String deviceId); + void onPermissionsChanged(int uid, String persistentDeviceId); } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 7a158c548a38..91adc37cb654 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -1738,8 +1738,9 @@ public final class PermissionManager { } @Override - public void onPermissionsChanged(int uid, String deviceId) { - mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget(); + public void onPermissionsChanged(int uid, String persistentDeviceId) { + mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId) + .sendToTarget(); } @Override @@ -1747,8 +1748,8 @@ public final class PermissionManager { switch (msg.what) { case MSG_PERMISSIONS_CHANGED: { final int uid = msg.arg1; - final String deviceId = msg.obj.toString(); - mListener.onPermissionsChanged(uid, deviceId); + final String persistentDeviceId = msg.obj.toString(); + mListener.onPermissionsChanged(uid, persistentDeviceId); return true; } default: diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 33c15d775ff1..ff6ec29bb8ac 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -18,6 +18,7 @@ package android.provider; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -36,6 +37,7 @@ import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.Application; import android.app.AutomaticZenRule; +import android.app.Flags; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.SearchManager; @@ -1904,6 +1906,36 @@ public final class Settings { = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS"; /** + * Activity Action: Shows the settings page for an {@link AutomaticZenRule} mode. + * <p> + * Users can change the behavior of the mode when it's activated and access the owning app's + * additional configuration screen, where triggering criteria can be modified (see + * {@link AutomaticZenRule#setConfigurationActivity(ComponentName)}). + * <p> + * A matching Activity will only be found if + * {@link NotificationManager#areAutomaticZenRulesUserManaged()} is true. + * <p> + * Input: Intent's data URI set with an application name, using the "package" schema (like + * "package:com.my.app"). + * Input: The id of the rule, provided in {@link #EXTRA_AUTOMATIC_ZEN_RULE_ID}. + * <p> + * Output: Nothing. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_AUTOMATIC_ZEN_RULE_SETTINGS + = "android.settings.AUTOMATIC_ZEN_RULE_SETTINGS"; + + /** + * Activity Extra: The String id of the {@link AutomaticZenRule mode} settings to display. + * <p> + * This must be passed as an extra field to the {@link #ACTION_AUTOMATIC_ZEN_RULE_SETTINGS}. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID + = "android.provider.extra.AUTOMATIC_ZEN_RULE_ID"; + + /** * Activity Action: Show settings for video captioning. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS index bb0e6aba436b..cb0b5fa6a029 100644 --- a/core/java/android/service/notification/OWNERS +++ b/core/java/android/service/notification/OWNERS @@ -2,6 +2,7 @@ juliacr@google.com yurilin@google.com +matiashe@google.com jeffdq@google.com dsandler@android.com dsandler@google.com diff --git a/core/java/android/service/timezone/TEST_MAPPING b/core/java/android/service/timezone/TEST_MAPPING index 21a8eab19837..e5910ea4a1fa 100644 --- a/core/java/android/service/timezone/TEST_MAPPING +++ b/core/java/android/service/timezone/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.service." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "CtsLocationTimeZoneManagerHostTest" } diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java index cc33af32ba93..10905e1b1908 100644 --- a/core/java/android/util/DataUnit.java +++ b/core/java/android/util/DataUnit.java @@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public enum DataUnit { KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } }, MEGABYTES { @Override public long toBytes(long v) { return v * 1_000_000; } }, diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index 4654dbfa9531..d2c5975ea356 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -48,6 +48,9 @@ import java.util.regex.Pattern; * They carry a payload of one or more int, long, or String values. The * event-log-tags file defines the payload contents for each type code. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass +@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( + "com.android.hoststubgen.nativesubstitution.EventLog_host") public class EventLog { /** @hide */ public EventLog() {} @@ -416,6 +419,7 @@ public class EventLog { /** * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done. */ + @android.ravenwood.annotation.RavenwoodReplace private static synchronized void readTagsFile() { if (sTagCodes != null && sTagNames != null) return; @@ -441,8 +445,7 @@ public class EventLog { try { int num = Integer.parseInt(m.group(1)); String name = m.group(2); - sTagCodes.put(name, num); - sTagNames.put(num, name); + registerTagLocked(num, name); } catch (NumberFormatException e) { Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e); } @@ -454,4 +457,20 @@ public class EventLog { try { if (reader != null) reader.close(); } catch (IOException e) {} } } + + private static void registerTagLocked(int num, String name) { + sTagCodes.put(name, num); + sTagNames.put(num, name); + } + + private static synchronized void readTagsFile$ravenwood() { + // TODO: restore parsing logic once we carry into runtime + sTagCodes = new HashMap<String, Integer>(); + sTagNames = new HashMap<Integer, String>(); + + // Hard-code a few common tags + registerTagLocked(524288, "sysui_action"); + registerTagLocked(524290, "sysui_count"); + registerTagLocked(524291, "sysui_histogram"); + } } diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java index c04a71c4d31b..413ae1f5cbcf 100644 --- a/core/java/android/util/IntArray.java +++ b/core/java/android/util/IntArray.java @@ -26,6 +26,7 @@ import java.util.Arrays; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class IntArray implements Cloneable { private static final int MIN_CAPACITY_INCREMENT = 12; diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java index 3101c0da6986..4c7ef08ddfcb 100644 --- a/core/java/android/util/LongArray.java +++ b/core/java/android/util/LongArray.java @@ -30,6 +30,7 @@ import java.util.Arrays; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class LongArray implements Cloneable { private static final int MIN_CAPACITY_INCREMENT = 12; diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java index 3aeeccaba250..c0ceb9ea8204 100644 --- a/core/java/android/util/Slog.java +++ b/core/java/android/util/Slog.java @@ -31,6 +31,7 @@ import android.os.Build; * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class Slog { private Slog() { @@ -216,6 +217,7 @@ public final class Slog { * @see Log#wtf(String, String) */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @android.ravenwood.annotation.RavenwoodThrow public static int wtf(@Nullable String tag, @NonNull String msg) { return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, false, true); } @@ -223,6 +225,7 @@ public final class Slog { /** * Similar to {@link #wtf(String, String)}, but does not output anything to the log. */ + @android.ravenwood.annotation.RavenwoodThrow public static void wtfQuiet(@Nullable String tag, @NonNull String msg) { Log.wtfQuiet(Log.LOG_ID_SYSTEM, tag, msg, true); } @@ -241,6 +244,7 @@ public final class Slog { * @see Log#wtfStack(String, String) */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + @android.ravenwood.annotation.RavenwoodThrow public static int wtfStack(@Nullable String tag, @NonNull String msg) { return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, true, true); } @@ -259,6 +263,7 @@ public final class Slog { * * @see Log#wtf(String, Throwable) */ + @android.ravenwood.annotation.RavenwoodThrow public static int wtf(@Nullable String tag, @Nullable Throwable tr) { return Log.wtf(Log.LOG_ID_SYSTEM, tag, tr.getMessage(), tr, false, true); } @@ -279,6 +284,7 @@ public final class Slog { * @see Log#wtf(String, String, Throwable) */ @UnsupportedAppUsage + @android.ravenwood.annotation.RavenwoodThrow public static int wtf(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, tr, false, true); } diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index d06b0ce1a2d8..bff8db13ad14 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -41,6 +41,8 @@ import java.util.List; /** * A class containing utility methods related to time zones. */ +@android.ravenwood.annotation.RavenwoodKeepPartialClass +@android.ravenwood.annotation.RavenwoodKeepStaticInitializer public class TimeUtils { /** @hide */ public TimeUtils() {} /** {@hide} */ @@ -180,6 +182,7 @@ public class TimeUtils { private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; + @android.ravenwood.annotation.RavenwoodKeep static private int accumField(int amt, int suffix, boolean always, int zeropad) { if (amt > 999) { int num = 0; @@ -202,6 +205,7 @@ public class TimeUtils { return 0; } + @android.ravenwood.annotation.RavenwoodKeep static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos, boolean always, int zeropad) { if (always || amt > 0) { @@ -242,6 +246,7 @@ public class TimeUtils { return pos; } + @android.ravenwood.annotation.RavenwoodKeep private static int formatDurationLocked(long duration, int fieldLen) { if (sFormatStr.length < fieldLen) { sFormatStr = new char[fieldLen]; @@ -314,6 +319,7 @@ public class TimeUtils { } /** @hide Just for debugging; not internationalized. */ + @android.ravenwood.annotation.RavenwoodKeep public static void formatDuration(long duration, StringBuilder builder) { synchronized (sFormatSync) { int len = formatDurationLocked(duration, 0); @@ -322,6 +328,7 @@ public class TimeUtils { } /** @hide Just for debugging; not internationalized. */ + @android.ravenwood.annotation.RavenwoodKeep public static void formatDuration(long duration, StringBuilder builder, int fieldLen) { synchronized (sFormatSync) { int len = formatDurationLocked(duration, fieldLen); @@ -331,6 +338,7 @@ public class TimeUtils { /** @hide Just for debugging; not internationalized. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + @android.ravenwood.annotation.RavenwoodKeep public static void formatDuration(long duration, PrintWriter pw, int fieldLen) { synchronized (sFormatSync) { int len = formatDurationLocked(duration, fieldLen); @@ -340,6 +348,7 @@ public class TimeUtils { /** @hide Just for debugging; not internationalized. */ @TestApi + @android.ravenwood.annotation.RavenwoodKeep public static String formatDuration(long duration) { synchronized (sFormatSync) { int len = formatDurationLocked(duration, 0); @@ -349,11 +358,13 @@ public class TimeUtils { /** @hide Just for debugging; not internationalized. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + @android.ravenwood.annotation.RavenwoodKeep public static void formatDuration(long duration, PrintWriter pw) { formatDuration(duration, pw, 0); } /** @hide Just for debugging; not internationalized. */ + @android.ravenwood.annotation.RavenwoodKeep public static void formatDuration(long time, long now, StringBuilder sb) { if (time == 0) { sb.append("--"); @@ -363,6 +374,7 @@ public class TimeUtils { } /** @hide Just for debugging; not internationalized. */ + @android.ravenwood.annotation.RavenwoodKeep public static void formatDuration(long time, long now, PrintWriter pw) { if (time == 0) { pw.print("--"); @@ -372,16 +384,19 @@ public class TimeUtils { } /** @hide Just for debugging; not internationalized. */ + @android.ravenwood.annotation.RavenwoodKeep public static String formatUptime(long time) { return formatTime(time, SystemClock.uptimeMillis()); } /** @hide Just for debugging; not internationalized. */ + @android.ravenwood.annotation.RavenwoodKeep public static String formatRealtime(long time) { return formatTime(time, SystemClock.elapsedRealtime()); } /** @hide Just for debugging; not internationalized. */ + @android.ravenwood.annotation.RavenwoodKeep public static String formatTime(long time, long referenceTime) { long diff = time - referenceTime; if (diff > 0) { @@ -402,6 +417,7 @@ public class TimeUtils { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @android.ravenwood.annotation.RavenwoodKeep public static String logTimeOfDay(long millis) { Calendar c = Calendar.getInstance(); if (millis >= 0) { @@ -413,6 +429,7 @@ public class TimeUtils { } /** {@hide} */ + @android.ravenwood.annotation.RavenwoodKeep public static String formatForLogging(long millis) { if (millis <= 0) { return "unknown"; @@ -426,6 +443,7 @@ public class TimeUtils { * * @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static void dumpTime(PrintWriter pw, long time) { pw.print(sDumpDateFormat.format(new Date(time))); } @@ -457,6 +475,7 @@ public class TimeUtils { * * @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static void dumpTimeWithDelta(PrintWriter pw, long time, long now) { pw.print(sDumpDateFormat.format(new Date(time))); if (time == now) { diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 43bfe139c223..53aed498f110 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -23,7 +23,7 @@ import static java.util.Collections.EMPTY_LIST; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; -import android.annotation.Hide; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -752,6 +752,7 @@ public class AccessibilityNodeInfo implements Parcelable { * {@link #isGranularScrollingSupported()} to check if granular scrolling is supported. * </p> */ + @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING) public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT"; @@ -2608,6 +2609,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if all scroll actions that could support * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise. */ + @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING) public boolean isGranularScrollingSupported() { return getBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING); } @@ -2626,6 +2628,7 @@ public class AccessibilityNodeInfo implements Parcelable { * * @throws IllegalStateException If called from an AccessibilityService. */ + @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING) public void setGranularScrollingSupported(boolean granularScrollingSupported) { setBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING, granularScrollingSupported); @@ -6119,6 +6122,7 @@ public class AccessibilityNodeInfo implements Parcelable { * This should be used for {@code mItemCount} and * {@code mImportantForAccessibilityItemCount} when values for those fields are not known. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public static final int UNDEFINED = -1; private int mRowCount; @@ -6229,8 +6233,8 @@ public class AccessibilityNodeInfo implements Parcelable { * the item count is not known. * @param importantForAccessibilityItemCount The count of the collection's views considered * important for accessibility. + * @hide */ - @Hide public CollectionInfo(int rowCount, int columnCount, boolean hierarchical, int selectionMode, int itemCount, int importantForAccessibilityItemCount) { mRowCount = rowCount; @@ -6287,6 +6291,7 @@ public class AccessibilityNodeInfo implements Parcelable { * * @return The count of items, which may be {@code UNDEFINED} if the count is not known. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public int getItemCount() { return mItemCount; } @@ -6297,6 +6302,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return The count of items important for accessibility, which may be {@code UNDEFINED} * if the count is not known. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public int getImportantForAccessibilityItemCount() { return mImportantForAccessibilityItemCount; } @@ -6323,6 +6329,7 @@ public class AccessibilityNodeInfo implements Parcelable { * The builder for CollectionInfo. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public static final class Builder { private int mRowCount = 0; private int mColumnCount = 0; @@ -6334,6 +6341,7 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Creates a new Builder. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public Builder() { } @@ -6343,6 +6351,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setRowCount(int rowCount) { mRowCount = rowCount; return this; @@ -6354,6 +6363,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setColumnCount(int columnCount) { mColumnCount = columnCount; return this; @@ -6364,6 +6374,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setHierarchical(boolean hierarchical) { mHierarchical = hierarchical; return this; @@ -6375,6 +6386,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setSelectionMode(int selectionMode) { mSelectionMode = selectionMode; return this; @@ -6389,6 +6401,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setItemCount(int itemCount) { mItemCount = itemCount; return this; @@ -6401,6 +6414,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setImportantForAccessibilityItemCount( int importantForAccessibilityItemCount) { mImportantForAccessibilityItemCount = importantForAccessibilityItemCount; @@ -6411,6 +6425,7 @@ public class AccessibilityNodeInfo implements Parcelable { * Creates a new {@link CollectionInfo} instance. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo build() { CollectionInfo collectionInfo = new CollectionInfo(mRowCount, mColumnCount, mHierarchical); diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index 950fa4b1d711..c337cb45d3a6 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -17,6 +17,13 @@ flag { } flag { + namespace: "accessibility" + name: "collection_info_item_counts" + description: "Fields for total items and the number of important for accessibility items in a collection" + bug: "302376158" +} + +flag { name: "deduplicate_accessibility_warning_dialog" namespace: "accessibility" description: "Removes duplicate definition of the accessibility warning dialog." @@ -39,6 +46,13 @@ flag { flag { namespace: "accessibility" + name: "granular_scrolling" + description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen" + bug: "302376158" +} + +flag { + namespace: "accessibility" name: "update_always_on_a11y_service" description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut." bug: "298869916" diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 42a1f1e58967..0da03fb5aaeb 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -37,5 +37,14 @@ flag { namespace: "window_surfaces" name: "remove_capture_display" description: "Remove uses of ScreenCapture#captureDisplay" + is_fixed_read_only: true bug: "293445881" -}
\ No newline at end of file +} + +flag { + namespace: "window_surfaces" + name: "allow_disable_activity_record_input_sink" + description: "Whether to allow system activity to disable ActivityRecordInputSink" + is_fixed_read_only: true + bug: "262477923" +} diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index b600b22751ff..933cc49f8602 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -9,6 +9,16 @@ flag { bug: "260873529" } +# Using a fixed read only flag because there are ClientTransaction scheduling before +# WindowManagerService creation. +flag { + namespace: "windowing_sdk" + name: "bundle_client_transaction_flag" + description: "To bundle multiple ClientTransactionItems into one ClientTransaction" + bug: "260873529" + is_fixed_read_only: true +} + flag { namespace: "windowing_sdk" name: "activity_embedding_overlay_presentation_flag" diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index df6c1538fc6d..7be27be2e798 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -54,22 +54,6 @@ public class SystemUiSystemPropertiesFlags { */ public static final class NotificationFlags { - /** - * FOR DEVELOPMENT / TESTING ONLY!!! - * Forcibly demote *ALL* FSI notifications as if no apps have the app op permission. - * NOTE: enabling this implies SHOW_STICKY_HUN_FOR_DENIED_FSI in SystemUI - */ - public static final Flag FSI_FORCE_DEMOTE = - devFlag("persist.sysui.notification.fsi_force_demote"); - - /** Gating the feature which shows FSI-denied notifications as Sticky HUNs */ - public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI = - releasedFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi"); - - /** Gating the redaction of OTP notifications on the lockscreen */ - public static final Flag OTP_REDACTION = - devFlag("persist.sysui.notification.otp_redaction"); - /** Gating the logging of DND state change events. */ public static final Flag LOG_DND_STATE_EVENTS = releasedFlag("persist.sysui.notification.log_dnd_state_events"); diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 5c1d91ff540e..5b68e8ed1ad8 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -184,11 +184,11 @@ status_t NativeInputEventReceiver::reportTimeline(int32_t inputEventId, nsecs_t void NativeInputEventReceiver::setFdEvents(int events) { if (mFdEvents != events) { mFdEvents = events; - int fd = mInputConsumer.getChannel()->getFd(); + auto&& fd = mInputConsumer.getChannel()->getFd(); if (events) { - mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr); + mMessageQueue->getLooper()->addFd(fd.get(), 0, events, this, nullptr); } else { - mMessageQueue->getLooper()->removeFd(fd); + mMessageQueue->getLooper()->removeFd(fd.get()); } } } diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 833952def02b..6bdf8214a1bf 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -102,8 +102,8 @@ NativeInputEventSender::~NativeInputEventSender() { } status_t NativeInputEventSender::initialize() { - int receiveFd = mInputPublisher.getChannel()->getFd(); - mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL); + auto&& receiveFd = mInputPublisher.getChannel()->getFd(); + mMessageQueue->getLooper()->addFd(receiveFd.get(), 0, ALOOPER_EVENT_INPUT, this, NULL); return OK; } @@ -112,7 +112,7 @@ void NativeInputEventSender::dispose() { LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Disposing input event sender."; } - mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd()); + mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd().get()); } status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7b075e6f4872..4d208c6ed30a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2301,6 +2301,7 @@ <!-- Allows system apps to call methods to register itself as a mDNS offload engine. <p>Not for use by third-party or privileged applications. @SystemApi + @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") @hide This should only be used by system apps. --> <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 68bad45b6e00..6cd6eb4b8df9 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5465,6 +5465,7 @@ <item>1,1,1.0,.15,15</item> <item>0,0,0.7,0,1</item> <item>0,0,0.83333,0,1</item> + <item>0,0,1.1667,0,1</item> </string-array> <!-- The integer index of the selected option in config_udfps_touch_detection_options --> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 4b02257978d2..2327b20bada6 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -42,6 +42,7 @@ import android.app.IApplicationThread; import android.app.PictureInPictureParams; import android.app.ResourcesManager; import android.app.servertransaction.ActivityConfigurationChangeItem; +import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; @@ -73,6 +74,7 @@ import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.internal.content.ReferrerIntent; +import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; @@ -227,7 +229,8 @@ public class ActivityThreadTest { try { // Send process level config change. ClientTransaction transaction = newTransaction(activityThread); - transaction.addCallback(ConfigurationChangeItem.obtain(newConfig, DEVICE_ID_INVALID)); + addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( + newConfig, DEVICE_ID_INVALID)); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -243,7 +246,7 @@ public class ActivityThreadTest { newConfig.seq++; newConfig.smallestScreenWidthDp++; transaction = newTransaction(activityThread); - transaction.addCallback(ActivityConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), newConfig)); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -444,16 +447,16 @@ public class ActivityThreadTest { activity.mTestLatch = new CountDownLatch(1); ClientTransaction transaction = newTransaction(activityThread); - transaction.addCallback(ConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( processConfigLandscape, DEVICE_ID_INVALID)); appThread.scheduleTransaction(transaction); transaction = newTransaction(activityThread); - transaction.addCallback(ActivityConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), activityConfigLandscape)); - transaction.addCallback(ConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( processConfigPortrait, DEVICE_ID_INVALID)); - transaction.addCallback(ActivityConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), activityConfigPortrait)); appThread.scheduleTransaction(transaction); @@ -840,8 +843,8 @@ public class ActivityThreadTest { false /* shouldSendCompatFakeFocus*/); final ClientTransaction transaction = newTransaction(activity); - transaction.addCallback(callbackItem); - transaction.setLifecycleStateRequest(resumeStateRequest); + addClientTransactionItem(transaction, callbackItem); + addClientTransactionItem(transaction, resumeStateRequest); return transaction; } @@ -853,7 +856,7 @@ public class ActivityThreadTest { false /* shouldSendCompatFakeFocus */); final ClientTransaction transaction = newTransaction(activity); - transaction.setLifecycleStateRequest(resumeStateRequest); + addClientTransactionItem(transaction, resumeStateRequest); return transaction; } @@ -864,7 +867,7 @@ public class ActivityThreadTest { activity.getActivityToken(), 0 /* configChanges */); final ClientTransaction transaction = newTransaction(activity); - transaction.setLifecycleStateRequest(stopStateRequest); + addClientTransactionItem(transaction, stopStateRequest); return transaction; } @@ -876,7 +879,7 @@ public class ActivityThreadTest { activity.getActivityToken(), config); final ClientTransaction transaction = newTransaction(activity); - transaction.addCallback(item); + addClientTransactionItem(transaction, item); return transaction; } @@ -888,7 +891,7 @@ public class ActivityThreadTest { resume); final ClientTransaction transaction = newTransaction(activity); - transaction.addCallback(item); + addClientTransactionItem(transaction, item); return transaction; } @@ -903,6 +906,17 @@ public class ActivityThreadTest { return ClientTransaction.obtain(activityThread.getApplicationThread()); } + private static void addClientTransactionItem(@NonNull ClientTransaction transaction, + @NonNull ClientTransactionItem item) { + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(item); + } else if (item.isActivityLifecycleItem()) { + transaction.setLifecycleStateRequest((ActivityLifecycleItem) item); + } else { + transaction.addCallback(item); + } + } + // Test activity public static class TestActivity extends Activity { static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter"; diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java index 2ec58d43477d..5a202c5e8834 100644 --- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java +++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java @@ -20,6 +20,7 @@ import static android.view.Surface.ROTATION_90; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; @@ -43,11 +44,29 @@ import java.util.Random; @LargeTest public class ParcelableUsageEventListTest { private static final int SMALL_TEST_EVENT_COUNT = 100; - private static final int LARGE_TEST_EVENT_COUNT = 10000; + private static final int LARGE_TEST_EVENT_COUNT = 30000; private Random mRandom = new Random(); @Test + public void testNullList() throws Exception { + Parcel parcel = Parcel.obtain(); + try { + parcel.writeParcelable(new ParcelableUsageEventList(null), 0); + fail("Expected IllegalArgumentException with null list."); + } catch (IllegalArgumentException expected) { + // Expected. + } finally { + parcel.recycle(); + } + } + + @Test + public void testEmptyList() throws Exception { + testParcelableUsageEventList(0); + } + + @Test public void testSmallList() throws Exception { testParcelableUsageEventList(SMALL_TEST_EVENT_COUNT); } @@ -58,15 +77,15 @@ public class ParcelableUsageEventListTest { } private void testParcelableUsageEventList(int eventCount) throws Exception { - List<Event> smallList = new ArrayList<>(); + List<Event> eventList = new ArrayList<>(); for (int i = 0; i < eventCount; i++) { - smallList.add(generateUsageEvent()); + eventList.add(generateUsageEvent()); } ParcelableUsageEventList slice; Parcel parcel = Parcel.obtain(); try { - parcel.writeParcelable(new ParcelableUsageEventList(smallList), 0); + parcel.writeParcelable(new ParcelableUsageEventList(eventList), 0); parcel.setDataPosition(0); slice = parcel.readParcelable(getClass().getClassLoader(), ParcelableUsageEventList.class); @@ -79,7 +98,7 @@ public class ParcelableUsageEventListTest { assertEquals(eventCount, slice.getList().size()); for (int i = 0; i < eventCount; i++) { - compareUsageEvent(smallList.get(i), slice.getList().get(i)); + compareUsageEvent(eventList.get(i), slice.getList().get(i)); } } diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp index 580e73c331a1..06340a222ac6 100644 --- a/core/tests/utiltests/Android.bp +++ b/core/tests/utiltests/Android.bp @@ -35,6 +35,7 @@ android_test { "androidx.test.ext.junit", "truth", "servicestests-utils", + "ravenwood-junit", ], libs: [ @@ -50,3 +51,22 @@ android_test { test_suites: ["device-tests"], } + +android_ravenwood_test { + name: "FrameworksUtilTestsRavenwood", + static_libs: [ + "androidx.annotation_annotation", + "androidx.test.rules", + ], + srcs: [ + "src/android/util/DataUnitTest.java", + "src/android/util/EventLogTest.java", + "src/android/util/IndentingPrintWriterTest.java", + "src/android/util/IntArrayTest.java", + "src/android/util/LocalLogTest.java", + "src/android/util/LongArrayTest.java", + "src/android/util/SlogTest.java", + "src/android/util/TimeUtilsTest.java", + ], + auto_gen_config: true, +} diff --git a/core/tests/coretests/src/android/util/DataUnitTest.java b/core/tests/utiltests/src/android/util/DataUnitTest.java index 034cbddc0144..af9ebc833d4b 100644 --- a/core/tests/coretests/src/android/util/DataUnitTest.java +++ b/core/tests/utiltests/src/android/util/DataUnitTest.java @@ -16,12 +16,18 @@ package android.util; +import static org.junit.Assert.assertEquals; + import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest -public class DataUnitTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class DataUnitTest { + @Test public void testSi() throws Exception { assertEquals(12_000L, DataUnit.KILOBYTES.toBytes(12)); assertEquals(12_000_000L, DataUnit.MEGABYTES.toBytes(12)); @@ -29,6 +35,7 @@ public class DataUnitTest extends TestCase { assertEquals(12_000_000_000_000L, DataUnit.TERABYTES.toBytes(12)); } + @Test public void testIec() throws Exception { assertEquals(12_288L, DataUnit.KIBIBYTES.toBytes(12)); assertEquals(12_582_912L, DataUnit.MEBIBYTES.toBytes(12)); diff --git a/core/tests/coretests/src/android/util/EventLogTest.java b/core/tests/utiltests/src/android/util/EventLogTest.java index 94e72c4a8d52..0ebf2c163d19 100644 --- a/core/tests/coretests/src/android/util/EventLogTest.java +++ b/core/tests/utiltests/src/android/util/EventLogTest.java @@ -16,23 +16,42 @@ package android.util; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.EventLog.Event; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import junit.framework.AssertionFailedError; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; /** Unit tests for {@link android.util.EventLog} */ +@SmallTest +@RunWith(AndroidJUnit4.class) public class EventLogTest { + @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Test + public void testSimple() throws Throwable { + EventLog.writeEvent(42, 42); + EventLog.writeEvent(42, 42L); + EventLog.writeEvent(42, 42f); + EventLog.writeEvent(42, "forty-two"); + EventLog.writeEvent(42, 42, "forty-two", null, 42); + } @Test + @IgnoreUnderRavenwood(reason = "Reading not yet supported") public void testWithNewData() throws Throwable { Event event = createEvent(() -> { EventLog.writeEvent(314, 123); diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/utiltests/src/android/util/LocalLogTest.java index d4861cd391a8..5025a3d0980c 100644 --- a/core/tests/coretests/src/android/util/LocalLogTest.java +++ b/core/tests/utiltests/src/android/util/LocalLogTest.java @@ -16,9 +16,13 @@ package android.util; +import static org.junit.Assert.assertTrue; + import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.PrintWriter; import java.io.StringWriter; @@ -27,13 +31,16 @@ import java.util.Collections; import java.util.List; @LargeTest -public class LocalLogTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class LocalLogTest { + @Test public void testA_localTimestamps() { boolean localTimestamps = true; doTestA(localTimestamps); } + @Test public void testA_nonLocalTimestamps() { boolean localTimestamps = false; doTestA(localTimestamps); @@ -49,6 +56,7 @@ public class LocalLogTest extends TestCase { testcase(new LocalLog(10, localTimestamps), lines, want); } + @Test public void testB() { String[] lines = { "foo", @@ -59,6 +67,7 @@ public class LocalLogTest extends TestCase { testcase(new LocalLog(0), lines, want); } + @Test public void testC() { String[] lines = { "dropped", diff --git a/core/tests/utiltests/src/android/util/SlogTest.java b/core/tests/utiltests/src/android/util/SlogTest.java new file mode 100644 index 000000000000..6f761e348dbe --- /dev/null +++ b/core/tests/utiltests/src/android/util/SlogTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 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.util; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class SlogTest { + private static final String TAG = "tag"; + private static final String MSG = "msg"; + private static final Throwable THROWABLE = new Throwable(); + + @Test + public void testSimple() { + Slog.v(TAG, MSG); + Slog.d(TAG, MSG); + Slog.i(TAG, MSG); + Slog.w(TAG, MSG); + Slog.e(TAG, MSG); + } + + @Test + public void testThrowable() { + Slog.v(TAG, MSG, THROWABLE); + Slog.d(TAG, MSG, THROWABLE); + Slog.i(TAG, MSG, THROWABLE); + Slog.w(TAG, MSG, THROWABLE); + Slog.e(TAG, MSG, THROWABLE); + } +} diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java new file mode 100644 index 000000000000..e8246c83e086 --- /dev/null +++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 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.util; + +import static org.junit.Assert.assertEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.function.Consumer; + +@RunWith(AndroidJUnit4.class) +public class TimeUtilsTest { + public static final long SECOND_IN_MILLIS = 1000; + public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60; + public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; + public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24; + public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7; + + @Test + public void testFormatTime() { + assertEquals("1672556400000 (now)", + TimeUtils.formatTime(1672556400000L, 1672556400000L)); + assertEquals("1672556400000 (in 10 ms)", + TimeUtils.formatTime(1672556400000L, 1672556400000L - 10)); + assertEquals("1672556400000 (10 ms ago)", + TimeUtils.formatTime(1672556400000L, 1672556400000L + 10)); + + // Uses formatter above, so we just care that it doesn't crash + TimeUtils.formatRealtime(1672556400000L); + TimeUtils.formatUptime(1672556400000L); + } + + @Test + public void testFormatDuration_Zero() { + assertEquals("0", TimeUtils.formatDuration(0)); + } + + @Test + public void testFormatDuration_Negative() { + assertEquals("-10ms", TimeUtils.formatDuration(-10)); + } + + @Test + public void testFormatDuration() { + long accum = 900; + assertEquals("+900ms", TimeUtils.formatDuration(accum)); + + accum += 59 * SECOND_IN_MILLIS; + assertEquals("+59s900ms", TimeUtils.formatDuration(accum)); + + accum += 59 * MINUTE_IN_MILLIS; + assertEquals("+59m59s900ms", TimeUtils.formatDuration(accum)); + + accum += 23 * HOUR_IN_MILLIS; + assertEquals("+23h59m59s900ms", TimeUtils.formatDuration(accum)); + + accum += 6 * DAY_IN_MILLIS; + assertEquals("+6d23h59m59s900ms", TimeUtils.formatDuration(accum)); + } + + @Test + public void testDumpTime() { + assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> { + TimeUtils.dumpTime(pw, 1672556400000L); + })); + assertEquals("2023-01-01 00:00:00.000 (now)", runWithPrintWriter((pw) -> { + TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L); + })); + assertEquals("2023-01-01 00:00:00.000 (-10ms)", runWithPrintWriter((pw) -> { + TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L + 10); + })); + } + + @Test + public void testFormatForLogging() { + assertEquals("unknown", TimeUtils.formatForLogging(0)); + assertEquals("unknown", TimeUtils.formatForLogging(-1)); + assertEquals("unknown", TimeUtils.formatForLogging(Long.MIN_VALUE)); + assertEquals("2023-01-01 00:00:00", TimeUtils.formatForLogging(1672556400000L)); + } + + @Test + public void testLogTimeOfDay() { + assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L)); + } + + public static String runWithPrintWriter(Consumer<PrintWriter> consumer) { + final StringWriter sw = new StringWriter(); + consumer.accept(new PrintWriter(sw)); + return sw.toString(); + } + + public static String runWithStringBuilder(Consumer<StringBuilder> consumer) { + final StringBuilder sb = new StringBuilder(); + consumer.accept(sb); + return sb.toString(); + } +} diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java index 07f1d4a09006..8dc9579e6b52 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java @@ -63,7 +63,7 @@ public class HideInCommentsChecker extends BugChecker implements @Override public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) { - final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree); + final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state); final String sourceCode = state.getSourceCode().toString(); for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) { for (Tokens.Comment comment : token.comments()) { @@ -112,9 +112,9 @@ public class HideInCommentsChecker extends BugChecker implements } - private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) { + private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree, VisitorState state) { Map<Integer, Tree> javadoccableTrees = new HashMap<>(); - new SuppressibleTreePathScanner<Void, Void>() { + new SuppressibleTreePathScanner<Void, Void>(state) { @Override public Void visitClass(ClassTree classTree, Void unused) { javadoccableTrees.put(getStartPosition(classTree), classTree); diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index c366ccd235db..4d2d960822d1 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -42,3 +42,11 @@ flag { description: "Enables PiP UI state callback on entering" bug: "303718131" } + +flag { + name: "enable_pip2_implementation" + namespace: "multitasking" + description: "Enables the new implementation of PiP (PiP2)" + bug: "290220798" + is_fixed_read_only: true +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index c7ab6aa3934e..f5b877a70b84 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -963,7 +963,11 @@ public class BubbleExpandedView extends LinearLayout { && mTaskView.isAttachedToWindow()) { // post this to the looper, because if the device orientation just changed, we need to // let the current shell transition complete before updating the task view bounds. - post(() -> mTaskView.onLocationChanged()); + post(() -> { + if (mTaskView != null) { + mTaskView.onLocationChanged(); + } + }); } if (mIsOverflow) { // post this to the looper so that the view has a chance to be laid out before it can diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt index 108aa8275009..1e30d8feb132 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt @@ -21,13 +21,13 @@ import android.app.WindowConfiguration import android.content.ComponentName import android.content.Context import android.os.RemoteException -import android.os.SystemProperties import android.util.DisplayMetrics import android.util.Log import android.util.Pair import android.util.TypedValue import android.window.TaskSnapshot import com.android.internal.protolog.common.ProtoLog +import com.android.wm.shell.Flags import com.android.wm.shell.protolog.ShellProtoLogGroup import kotlin.math.abs @@ -37,7 +37,6 @@ object PipUtils { // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal. private const val EPSILON = 1e-7 - private const val ENABLE_PIP2_IMPLEMENTATION = "persist.wm.debug.enable_pip2_implementation" /** * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack. @@ -138,5 +137,5 @@ object PipUtils { @JvmStatic val isPip2ExperimentEnabled: Boolean - get() = SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false) + get() = Flags.enablePip2Implementation() }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 27dc870e81ae..b158f88a68c3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -250,10 +250,10 @@ public abstract class WMShellBaseModule { SyncTransactionQueue syncQueue, @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy, - DockStateReader dockStateReader, - CompatUIConfiguration compatUIConfiguration, - CompatUIShellCommandHandler compatUIShellCommandHandler, - AccessibilityManager accessibilityManager) { + Lazy<DockStateReader> dockStateReader, + Lazy<CompatUIConfiguration> compatUIConfiguration, + Lazy<CompatUIShellCommandHandler> compatUIShellCommandHandler, + Lazy<AccessibilityManager> accessibilityManager) { if (!context.getResources().getBoolean(R.bool.config_enableCompatUIController)) { return Optional.empty(); } @@ -268,10 +268,10 @@ public abstract class WMShellBaseModule { syncQueue, mainExecutor, transitionsLazy, - dockStateReader, - compatUIConfiguration, - compatUIShellCommandHandler, - accessibilityManager)); + dockStateReader.get(), + compatUIConfiguration.get(), + compatUIShellCommandHandler.get(), + accessibilityManager.get())); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java index b528089d153e..5c02dbcb5255 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java @@ -17,6 +17,7 @@ package com.android.wm.shell.transition; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static com.android.wm.shell.transition.Transitions.TransitionObserver; @@ -61,9 +62,10 @@ public class HomeTransitionObserver implements TransitionObserver, } final int mode = change.getMode(); + final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED); if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME - && TransitionUtil.isOpenOrCloseMode(mode)) { - notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode)); + && (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture)) { + notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode) || isBackGesture); } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt index a5629c8f8f15..b355ab0bf311 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt @@ -14,8 +14,6 @@ import android.view.SurfaceControl import android.window.TransitionInfo import android.window.TransitionInfo.FLAG_IS_WALLPAPER import androidx.test.filters.SmallTest -import com.android.server.testutils.any -import com.android.server.testutils.mock import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder @@ -28,8 +26,10 @@ import java.util.function.Supplier import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mock +import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.verifyZeroInteractions diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java index ea7c0d9c264e..421c44511a54 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java @@ -18,8 +18,10 @@ package com.android.wm.shell.transition; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -145,6 +147,26 @@ public class HomeTransitionObserverTest extends ShellTestCase { verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean()); } + @Test + public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException { + TransitionInfo info = mock(TransitionInfo.class); + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); + when(change.getTaskInfo()).thenReturn(taskInfo); + when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); + + when(change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)).thenReturn(true); + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_CHANGE); + + mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), + info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + + verify(mListener, times(1)).onHomeVisibilityChanged(true); + } + + /** * Helper class to initialize variables for the rest. */ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index d056248273b2..8748dab581bb 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -603,7 +603,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, std::unique_ptr<Asset> asset = assets->GetAssetsProvider()->Open(filename, mode); if (asset) { if (out_cookie != nullptr) { - *out_cookie = i; + *out_cookie = i - 1; } return asset; } diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index b63ee1bd3d98..a9d1a2aed8cc 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -25,8 +25,9 @@ #include "MinikinSkia.h" #include "SkPaint.h" -#include "SkStream.h" // Fot tests. +#include "SkStream.h" // For tests. #include "SkTypeface.h" +#include "utils/TypefaceUtils.h" #include <minikin/FontCollection.h> #include <minikin/FontFamily.h> @@ -186,7 +187,9 @@ void Typeface::setRobotoTypefaceForTest() { LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont); void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size)); - sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData)); + sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr(); + LOG_ALWAYS_FATAL_IF(fm == nullptr, "Could not load FreeType SkFontMgr"); + sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(fontData)); LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont); std::shared_ptr<minikin::MinikinFont> font = diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index de842e68b118..9f63dfdc0ccb 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1065,17 +1065,8 @@ public class AudioManager { * @see #isVolumeFixed() */ public void adjustVolume(int direction, @PublicVolumeFlags int flags) { - if (autoPublicVolumeApiHardening()) { - final IAudioService service = getService(); - try { - service.adjustVolume(direction, flags); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } else { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); - helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); - } + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); + helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); } /** @@ -1104,17 +1095,8 @@ public class AudioManager { */ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, @PublicVolumeFlags int flags) { - if (autoPublicVolumeApiHardening()) { - final IAudioService service = getService(); - try { - service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } else { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); - helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); - } + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); + helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); } /** @hide */ diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 11e3a088b5e2..b4ca485eb764 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -500,10 +500,6 @@ interface IAudioService { in String packageName, int uid, int pid, in UserHandle userHandle, int targetSdkVersion); - oneway void adjustVolume(int direction, int flags); - - oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags); - boolean isMusicActive(in boolean remotely); int getDeviceMaskForStream(in int streamType); diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt index 4533db674da2..3abdb6f9c9f2 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt @@ -37,17 +37,17 @@ val Intent.requestInfo: RequestInfo? RequestInfo::class.java ) -val Intent.getCredentialProviderDataList: List<ProviderData> +val Intent.getCredentialProviderDataList: List<GetCredentialProviderData> get() = this.extras?.getParcelableArrayList( ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, GetCredentialProviderData::class.java - ) ?: emptyList() + ) ?.filterIsInstance<GetCredentialProviderData>() ?: emptyList() -val Intent.createCredentialProviderDataList: List<ProviderData> +val Intent.createCredentialProviderDataList: List<CreateCredentialProviderData> get() = this.extras?.getParcelableArrayList( ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, CreateCredentialProviderData::class.java - ) ?: emptyList() + ) ?.filterIsInstance<CreateCredentialProviderData>() ?: emptyList() val Intent.resultReceiver: ResultReceiver? get() = this.getParcelableExtra( diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt index ee45fbb00ba6..d4bca2add6cb 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt @@ -18,7 +18,6 @@ package com.android.credentialmanager.mapper import android.content.Intent import android.credentials.ui.Entry -import android.credentials.ui.GetCredentialProviderData import androidx.credentials.provider.PasswordCredentialEntry import com.android.credentialmanager.factory.fromSlice import com.android.credentialmanager.ktx.getCredentialProviderDataList @@ -32,12 +31,10 @@ import com.google.common.collect.ImmutableMap fun Intent.toGet(): Request.Get { val credentialEntries = mutableListOf<Pair<String, Entry>>() for (providerData in getCredentialProviderDataList) { - if (providerData is GetCredentialProviderData) { - for (credentialEntry in providerData.credentialEntries) { - credentialEntries.add( - Pair(providerData.providerFlattenedComponentName, credentialEntry) - ) - } + for (credentialEntry in providerData.credentialEntries) { + credentialEntries.add( + Pair(providerData.providerFlattenedComponentName, credentialEntry) + ) } } diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm index 93a508263962..071f9f436e04 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm @@ -116,6 +116,9 @@ key W { base: 'w' shift, capslock: 'W' shift+capslock: 'w' + ralt: '\u1e83' + shift+ralt, capslock+ralt: '\u1e82' + shift+capslock+ralt: '\u1e83' } key E { @@ -147,6 +150,9 @@ key Y { base: 'y' shift, capslock: 'Y' shift+capslock: 'y' + ralt: '\u00fd' + shift+ralt, capslock+ralt: '\u00dd' + shift+capslock+ralt: '\u00fd' } key U { @@ -313,6 +319,9 @@ key C { base: 'c' shift, capslock: 'C' shift+capslock: 'c' + ralt: '\u00e7' + shift+ralt, capslock+ralt: '\u00c7' + shift+capslock+ralt: '\u00e7' } key V { diff --git a/packages/InputDevices/res/raw/keyboard_layout_french.kcm b/packages/InputDevices/res/raw/keyboard_layout_french.kcm index 490630456bb2..636f98d8bb95 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_french.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_french.kcm @@ -44,7 +44,7 @@ key 2 { label: '2' base: '\u00e9' shift: '2' - ralt: '~' + ralt: '\u0303' } key 3 { @@ -79,7 +79,7 @@ key 7 { label: '7' base: '\u00e8' shift: '7' - ralt: '`' + ralt: '\u0300' } key 8 { diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml index 7f23f747090b..ee49b23b3860 100644 --- a/packages/InputDevices/res/xml/keyboard_layouts.xml +++ b/packages/InputDevices/res/xml/keyboard_layouts.xml @@ -94,7 +94,7 @@ android:name="keyboard_layout_swiss_german" android:label="@string/keyboard_layout_swiss_german_label" android:keyboardLayout="@raw/keyboard_layout_swiss_german" - android:keyboardLocale="de-Latn-CH" + android:keyboardLocale="de-Latn-CH|gsw-Latn-CH" android:keyboardLayoutType="qwertz" /> <keyboard-layout diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java index f83e37b2fd5c..3774b88db93d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java @@ -37,9 +37,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; -/** - * VolumeControlProfile handles Bluetooth Volume Control Controller role - */ +/** VolumeControlProfile handles Bluetooth Volume Control Controller role */ public class VolumeControlProfile implements LocalBluetoothProfile { private static final String TAG = "VolumeControlProfile"; private static boolean DEBUG = true; @@ -77,8 +75,8 @@ public class VolumeControlProfile implements LocalBluetoothProfile { } device = mDeviceManager.addDevice(nextDevice); } - device.onProfileStateChanged(VolumeControlProfile.this, - BluetoothProfile.STATE_CONNECTED); + device.onProfileStateChanged( + VolumeControlProfile.this, BluetoothProfile.STATE_CONNECTED); device.refresh(); } @@ -95,32 +93,36 @@ public class VolumeControlProfile implements LocalBluetoothProfile { } } - VolumeControlProfile(Context context, CachedBluetoothDeviceManager deviceManager, + VolumeControlProfile( + Context context, + CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager) { mContext = context; mDeviceManager = deviceManager; mProfileManager = profileManager; - BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, - new VolumeControlProfile.VolumeControlProfileServiceListener(), - BluetoothProfile.VOLUME_CONTROL); + BluetoothAdapter.getDefaultAdapter() + .getProfileProxy( + context, + new VolumeControlProfile.VolumeControlProfileServiceListener(), + BluetoothProfile.VOLUME_CONTROL); } - /** - * Registers a {@link BluetoothVolumeControl.Callback} that will be invoked during the - * operation of this profile. + * Registers a {@link BluetoothVolumeControl.Callback} that will be invoked during the operation + * of this profile. * - * Repeated registration of the same <var>callback</var> object will have no effect after - * the first call to this method, even when the <var>executor</var> is different. API caller - * would have to call {@link #unregisterCallback(BluetoothVolumeControl.Callback)} with - * the same callback object before registering it again. + * <p>Repeated registration of the same <var>callback</var> object will have no effect after the + * first call to this method, even when the <var>executor</var> is different. API caller would + * have to call {@link #unregisterCallback(BluetoothVolumeControl.Callback)} with the same + * callback object before registering it again. * * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link BluetoothVolumeControl.Callback} * @throws IllegalArgumentException if a null executor or callback is given */ - public void registerCallback(@NonNull @CallbackExecutor Executor executor, + public void registerCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothVolumeControl.Callback callback) { if (mService == null) { Log.w(TAG, "Proxy not attached to service. Cannot register callback."); @@ -131,8 +133,9 @@ public class VolumeControlProfile implements LocalBluetoothProfile { /** * Unregisters the specified {@link BluetoothVolumeControl.Callback}. - * <p>The same {@link BluetoothVolumeControl.Callback} object used when calling - * {@link #registerCallback(Executor, BluetoothVolumeControl.Callback)} must be used. + * + * <p>The same {@link BluetoothVolumeControl.Callback} object used when calling {@link + * #registerCallback(Executor, BluetoothVolumeControl.Callback)} must be used. * * <p>Callbacks are automatically unregistered when application process goes away * @@ -153,8 +156,8 @@ public class VolumeControlProfile implements LocalBluetoothProfile { * @param device {@link BluetoothDevice} representing the remote device * @param volumeOffset volume offset to be set on the remote device */ - public void setVolumeOffset(BluetoothDevice device, - @IntRange(from = -255, to = 255) int volumeOffset) { + public void setVolumeOffset( + BluetoothDevice device, @IntRange(from = -255, to = 255) int volumeOffset) { if (mService == null) { Log.w(TAG, "Proxy not attached to service. Cannot set volume offset."); return; @@ -165,16 +168,13 @@ public class VolumeControlProfile implements LocalBluetoothProfile { } mService.setVolumeOffset(device, volumeOffset); } - /** - * Provides information about the possibility to set volume offset on the remote device. - * If the remote device supports Volume Offset Control Service, it is automatically - * connected. + * Provides information about the possibility to set volume offset on the remote device. If the + * remote device supports Volume Offset Control Service, it is automatically connected. * * @param device {@link BluetoothDevice} representing the remote device * @return {@code true} if volume offset function is supported and available to use on the - * remote device. When Bluetooth is off, the return value should always be - * {@code false}. + * remote device. When Bluetooth is off, the return value should always be {@code false}. */ public boolean isVolumeOffsetAvailable(BluetoothDevice device) { if (mService == null) { @@ -188,6 +188,28 @@ public class VolumeControlProfile implements LocalBluetoothProfile { return mService.isVolumeOffsetAvailable(device); } + /** + * Tells the remote device to set a volume. + * + * @param device {@link BluetoothDevice} representing the remote device + * @param volume volume to be set on the remote device + * @param isGroupOp whether to set the volume to remote devices within the same CSIP group + */ + public void setDeviceVolume( + BluetoothDevice device, + @IntRange(from = 0, to = 255) int volume, + boolean isGroupOp) { + if (mService == null) { + Log.w(TAG, "Proxy not attached to service. Cannot set volume offset."); + return; + } + if (device == null) { + Log.w(TAG, "Device is null. Cannot set volume offset."); + return; + } + mService.setDeviceVolume(device, volume, isGroupOp); + } + @Override public boolean accessProfileEnabled() { return false; @@ -199,10 +221,9 @@ public class VolumeControlProfile implements LocalBluetoothProfile { } /** - * Gets VolumeControlProfile devices matching connection states{ - * {@code BluetoothProfile.STATE_CONNECTED}, - * {@code BluetoothProfile.STATE_CONNECTING}, - * {@code BluetoothProfile.STATE_DISCONNECTING}} + * Gets VolumeControlProfile devices matching connection states{ {@code + * BluetoothProfile.STATE_CONNECTED}, {@code BluetoothProfile.STATE_CONNECTING}, {@code + * BluetoothProfile.STATE_DISCONNECTING}} * * @return Matching device list */ @@ -211,8 +232,11 @@ public class VolumeControlProfile implements LocalBluetoothProfile { return new ArrayList<BluetoothDevice>(0); } return mService.getDevicesMatchingConnectionStates( - new int[]{BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}); + new int[] { + BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING + }); } @Override @@ -285,7 +309,7 @@ public class VolumeControlProfile implements LocalBluetoothProfile { @Override public int getSummaryResourceForDevice(BluetoothDevice device) { - return 0; // VCP profile not displayed in UI + return 0; // VCP profile not displayed in UI } @Override diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java index c56062739735..fe1529d11cd8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java @@ -54,18 +54,14 @@ import java.util.concurrent.Executor; public class VolumeControlProfileTest { private static final int TEST_VOLUME_OFFSET = 10; + private static final int TEST_VOLUME_VALUE = 10; - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - @Mock - private CachedBluetoothDeviceManager mDeviceManager; - @Mock - private LocalBluetoothProfileManager mProfileManager; - @Mock - private BluetoothDevice mBluetoothDevice; - @Mock - private BluetoothVolumeControl mService; + @Mock private CachedBluetoothDeviceManager mDeviceManager; + @Mock private LocalBluetoothProfileManager mProfileManager; + @Mock private BluetoothDevice mBluetoothDevice; + @Mock private BluetoothVolumeControl mService; private final Context mContext = ApplicationProvider.getApplicationContext(); private BluetoothProfile.ServiceListener mServiceListener; @@ -177,14 +173,14 @@ public class VolumeControlProfileTest { @Test public void getConnectedDevices_returnCorrectList() { mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService); - int[] connectedStates = new int[] { - BluetoothProfile.STATE_CONNECTED, - BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}; - List<BluetoothDevice> connectedList = Arrays.asList( - mBluetoothDevice, - mBluetoothDevice, - mBluetoothDevice); + int[] connectedStates = + new int[] { + BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING + }; + List<BluetoothDevice> connectedList = + Arrays.asList(mBluetoothDevice, mBluetoothDevice, mBluetoothDevice); when(mService.getDevicesMatchingConnectionStates(connectedStates)) .thenReturn(connectedList); @@ -222,6 +218,16 @@ public class VolumeControlProfileTest { } @Test + public void setDeviceVolume_verifyIsCalled() { + mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService); + + mProfile.setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true); + + verify(mService) + .setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true); + } + + @Test public void isVolumeOffsetAvailable_verifyIsCalledAndReturnTrue() { mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService); when(mService.isVolumeOffsetAvailable(mBluetoothDevice)).thenReturn(true); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 2c15fc6a9cfd..8412cbaaea36 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -249,6 +249,8 @@ public class SecureSettings { Settings.Secure.HUB_MODE_TUTORIAL_STATE, Settings.Secure.STYLUS_BUTTONS_ENABLED, Settings.Secure.STYLUS_HANDWRITING_ENABLED, - Settings.Secure.DEFAULT_NOTE_TASK_PROFILE + Settings.Secure.DEFAULT_NOTE_TASK_PROFILE, + Settings.Secure.CREDENTIAL_SERVICE, + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 71c2ddc6de1f..9197554662d3 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -19,11 +19,13 @@ package android.provider.settings.validators; import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COMMA_SEPARATED_COMPONENT_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; @@ -62,7 +64,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ADAPTIVE_CHARGING_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.CAMERA_AUTOROTATE, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR); VALIDATORS.put( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE)); @@ -398,5 +399,8 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.STYLUS_HANDWRITING_ENABLED, new DiscreteValueValidator(new String[] {"-1", "0", "1"})); VALIDATORS.put(Secure.DEFAULT_NOTE_TASK_PROFILE, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.CREDENTIAL_SERVICE, CREDENTIAL_SERVICE_VALIDATOR); + VALIDATORS.put(Secure.CREDENTIAL_SERVICE_PRIMARY, NULLABLE_COMPONENT_NAME_VALIDATOR); + VALIDATORS.put(Secure.AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java index 49012b0159c2..a8a659ee1e5c 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java @@ -235,4 +235,30 @@ public class SettingsValidators { } } }; + + static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null || value.equals("")) { + return true; + } + + return COLON_SEPARATED_COMPONENT_LIST_VALIDATOR.validate(value); + } + }; + + static final Validator AUTOFILL_SERVICE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null || value.equals("")) { + return true; + } + + if (value.equals("credential-provider")) { + return true; + } + + return NULLABLE_COMPONENT_NAME_VALIDATOR.validate(value); + } + }; } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index f1b53edbb1cd..efed8c3c1ef4 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -851,8 +851,6 @@ public class SettingsBackupTest { Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, Settings.Secure.UI_TRANSLATION_ENABLED, - Settings.Secure.CREDENTIAL_SERVICE, - Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED, Settings.Secure.DND_CONFIGS_MIGRATED, Settings.Secure.NAVIGATION_MODE_RESTORE); diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java index 865f431183c6..3b3bf8ca15f7 100644 --- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java +++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java @@ -340,6 +340,60 @@ public class SettingsValidatorsTest { failIfOffendersPresent(offenders, "Settings.Secure"); } + @Test + public void testCredentialServiceValidator_returnsTrueIfNull() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate(null)); + } + + @Test + public void testCredentialServiceValidator_returnsTrueIfEmpty() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate("")); + } + + @Test + public void testCredentialServiceValidator_returnsTrueIfSingleComponentName() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate( + "android.credentials/android.credentials.Test")); + } + + @Test + public void testCredentialServiceValidator_returnsTrueIfMultipleComponentName() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate( + "android.credentials/android.credentials.Test" + + ":android.credentials/.Test2")); + } + + @Test + public void testCredentialServiceValidator_returnsFalseIfInvalidComponentName() { + assertFalse(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate("test")); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfNull() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate(null)); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfEmpty() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("")); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfPlaceholder() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("credential-provider")); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfSingleComponentName() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate( + "android.credentials/android.credentials.Test")); + } + + @Test + public void testAutofillServiceValidator_returnsFalseIfInvalidComponentName() { + assertFalse(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("test")); + } + private void failIfOffendersPresent(String offenders, String settingsType) { if (offenders.length() > 0) { fail("All " + settingsType + " settings that are backed up have to have a non-null" diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 68d4b6319b2f..a745ab5cbdd9 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -140,4 +140,11 @@ flag { namespace: "systemui" description: "Move LightRevealScrim to recommended architecture" bug: "281655028" -}
\ No newline at end of file +} + +flag { + name: "media_in_scene_container" + namespace: "systemui" + description: "Enable media in the scene container framework" + bug: "296122467" +} diff --git a/packages/SystemUI/communal/layout/Android.bp b/packages/SystemUI/communal/layout/Android.bp deleted file mode 100644 index 88dad6623d03..000000000000 --- a/packages/SystemUI/communal/layout/Android.bp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2023 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 { - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_library { - name: "CommunalLayoutLib", - srcs: [ - "src/**/*.kt", - ], - static_libs: [ - "androidx.arch.core_core-runtime", - "androidx.compose.animation_animation-graphics", - "androidx.compose.runtime_runtime", - "androidx.compose.material3_material3", - "jsr330", - "kotlinx-coroutines-android", - "kotlinx-coroutines-core", - ], - manifest: "AndroidManifest.xml", - kotlincflags: ["-Xjvm-default=all"], -} diff --git a/packages/SystemUI/communal/layout/AndroidManifest.xml b/packages/SystemUI/communal/layout/AndroidManifest.xml deleted file mode 100644 index 141be0762ae9..000000000000 --- a/packages/SystemUI/communal/layout/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- Copyright (C) 2023 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. ---> - -<manifest package="com.android.systemui.communal.layout" /> diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt deleted file mode 100644 index 91fe33cd2f4b..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2023 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.communal.layout - -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard - -/** Computes the arrangement of cards. */ -class CommunalLayoutEngine { - companion object { - /** - * Determines the size that each card should be rendered in, and distributes the cards into - * columns. - * - * Returns a nested list where the outer list contains columns, and the inner list contains - * cards in each column. - * - * Currently treats the first supported size as the size to be rendered in, ignoring other - * supported sizes. - * - * Cards are ordered by priority, from highest to lowest. - */ - fun distributeCardsIntoColumns( - cards: List<CommunalGridLayoutCard>, - ): List<List<CommunalGridLayoutCardInfo>> { - val result = ArrayList<ArrayList<CommunalGridLayoutCardInfo>>() - - var capacityOfLastColumn = 0 - val sorted = cards.sortedByDescending { it.priority } - for (card in sorted) { - val cardSize = card.supportedSizes.first() - if (capacityOfLastColumn >= cardSize.value) { - // Card fits in last column - capacityOfLastColumn -= cardSize.value - } else { - // Create a new column - result.add(arrayListOf()) - capacityOfLastColumn = CommunalGridLayoutCard.Size.FULL.value - cardSize.value - } - - result.last().add(CommunalGridLayoutCardInfo(card, cardSize)) - } - - return result - } - } - - /** - * A data class that wraps around a [CommunalGridLayoutCard] and also contains the size that the - * card should be rendered in. - */ - data class CommunalGridLayoutCardInfo( - val card: CommunalGridLayoutCard, - val size: CommunalGridLayoutCard.Size, - ) -} diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt deleted file mode 100644 index 33024f764710..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2023 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.communal.layout.ui.compose - -import android.util.SizeF -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.android.systemui.communal.layout.CommunalLayoutEngine -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig - -/** - * An arrangement of cards with a horizontal scroll, where each card is displayed in the right size - * and follows a specific order based on its priority, ensuring a seamless layout without any gaps. - */ -@Composable -fun CommunalGridLayout( - modifier: Modifier, - layoutConfig: CommunalGridLayoutConfig, - communalCards: List<CommunalGridLayoutCard>, -) { - val columns = CommunalLayoutEngine.distributeCardsIntoColumns(communalCards) - LazyRow( - modifier = modifier.height(layoutConfig.gridHeight), - horizontalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter), - ) { - for (column in columns) { - item { - Column( - modifier = Modifier.width(layoutConfig.cardWidth), - verticalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter), - ) { - for (cardInfo in column) { - Row( - modifier = Modifier.height(layoutConfig.cardHeight(cardInfo.size)), - ) { - cardInfo.card.Content( - modifier = Modifier.fillMaxSize(), - size = - SizeF( - layoutConfig.cardWidth.value, - layoutConfig.cardHeight(cardInfo.size).value, - ), - ) - } - } - } - } - } - } -} diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt deleted file mode 100644 index 4b2a156c1dbd..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 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.communal.layout.ui.compose.config - -import android.util.SizeF -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier - -/** A card that hosts content to be rendered in the communal grid layout. */ -abstract class CommunalGridLayoutCard { - /** - * Content to be hosted by the card. - * - * To host non-Compose views, see - * https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose. - * - * @param size The size given to the card. Content of the card should fill all this space, given - * that margins and paddings have been taken care of by the layout. - */ - @Composable abstract fun Content(modifier: Modifier, size: SizeF) - - /** - * Sizes supported by the card. - * - * If multiple sizes are available, they should be ranked in order of preference, from most to - * least preferred. - */ - abstract val supportedSizes: List<Size> - - /** - * Priority of the content hosted by the card. - * - * The value of priority is relative to other cards. Cards with a higher priority are generally - * ordered first. - */ - open val priority: Int = 0 - - /** - * Size of the card. - * - * @param value A numeric value that represents the size. Must be less than or equal to - * [Size.FULL]. - */ - enum class Size(val value: Int) { - /** The card takes up full height of the grid layout. */ - FULL(value = 6), - - /** The card takes up half of the vertical space of the grid layout. */ - HALF(value = 3), - - /** The card takes up a third of the vertical space of the grid layout. */ - THIRD(value = 2), - } -} diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt deleted file mode 100644 index 143df838169b..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 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.communal.layout.ui.compose.config - -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.times - -/** - * Configurations of the communal grid layout. - * - * The communal grid layout follows Material Design's responsive layout grid (see - * https://m2.material.io/design/layout/responsive-layout-grid.html), in which the layout is divided - * up by columns and gutters, and each card occupies one or multiple columns. - */ -data class CommunalGridLayoutConfig( - /** - * Size in dp of each grid column. - * - * Every card occupies one or more grid columns, which means that the width of each card is - * influenced by the size of the grid columns. - */ - val gridColumnSize: Dp, - - /** - * Size in dp of each grid gutter. - * - * A gutter is the space between columns that helps separate content. This is, therefore, also - * the size of the gaps between cards, both horizontally and vertically. - */ - val gridGutter: Dp, - - /** - * Height in dp of the grid layout. - * - * Cards with a full size take up the entire height of the grid layout. - */ - val gridHeight: Dp, - - /** - * Number of grid columns that each card occupies. - * - * It's important to note that all the cards take up the same number of grid columns, or in - * simpler terms, they all have the same width. - */ - val gridColumnsPerCard: Int, -) { - /** - * Width in dp of each card. - * - * It's important to note that all the cards take up the same number of grid columns, or in - * simpler terms, they all have the same width. - */ - val cardWidth = gridColumnSize * gridColumnsPerCard + gridGutter * (gridColumnsPerCard - 1) - - /** Returns the height of a card in dp, based on its size. */ - fun cardHeight(cardSize: CommunalGridLayoutCard.Size): Dp { - return when (cardSize) { - CommunalGridLayoutCard.Size.FULL -> cardHeightBy(denominator = 1) - CommunalGridLayoutCard.Size.HALF -> cardHeightBy(denominator = 2) - CommunalGridLayoutCard.Size.THIRD -> cardHeightBy(denominator = 3) - } - } - - /** Returns the height of a card in dp when the layout is evenly divided by [denominator]. */ - private fun cardHeightBy(denominator: Int): Dp { - return (gridHeight - (denominator - 1) * gridGutter) / denominator - } -} diff --git a/packages/SystemUI/communal/layout/tests/Android.bp b/packages/SystemUI/communal/layout/tests/Android.bp deleted file mode 100644 index 9a05504cad8b..000000000000 --- a/packages/SystemUI/communal/layout/tests/Android.bp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2023 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 { - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_test { - name: "CommunalLayoutLibTests", - srcs: [ - "**/*.kt", - ], - static_libs: [ - "CommunalLayoutLib", - "androidx.test.runner", - "androidx.test.rules", - "androidx.test.ext.junit", - "frameworks-base-testutils", - "junit", - "kotlinx_coroutines_test", - "mockito-target-extended-minus-junit4", - "platform-test-annotations", - "testables", - "truth", - ], - libs: [ - "android.test.mock", - "android.test.base", - "android.test.runner", - ], - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - manifest: "AndroidManifest.xml", -} diff --git a/packages/SystemUI/communal/layout/tests/AndroidManifest.xml b/packages/SystemUI/communal/layout/tests/AndroidManifest.xml deleted file mode 100644 index b19007c1ff1b..000000000000 --- a/packages/SystemUI/communal/layout/tests/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest - xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.communal.layout.tests"> - - <application android:debuggable="true" android:largeHeap="true"> - <uses-library android:name="android.test.mock" /> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="android.testing.TestableInstrumentation" - android:targetPackage="com.android.systemui.communal.layout.tests" - android:label="Tests for CommunalLayoutLib"> - </instrumentation> - -</manifest> diff --git a/packages/SystemUI/communal/layout/tests/AndroidTest.xml b/packages/SystemUI/communal/layout/tests/AndroidTest.xml deleted file mode 100644 index 1352b238f6fe..000000000000 --- a/packages/SystemUI/communal/layout/tests/AndroidTest.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<configuration description="Runs tests for CommunalLayoutLib"> - - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="install-arg" value="-t" /> - <option name="test-file-name" value="CommunalLayoutLibTests.apk" /> - </target_preparer> - - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="framework-base-presubmit" /> - <option name="test-tag" value="CommunalLayoutLibTests" /> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.android.systemui.communal.layout.tests" /> - <option name="runner" value="android.testing.TestableInstrumentation" /> - <option name="hidden-api-checks" value="false"/> - </test> - -</configuration> diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt deleted file mode 100644 index 50b7c5f02068..000000000000 --- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt +++ /dev/null @@ -1,145 +0,0 @@ -package com.android.systemui.communal.layout - -import android.util.SizeF -import androidx.compose.material3.Card -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class CommunalLayoutEngineTest { - @Test - fun distribution_fullLayout() { - val cards = - listOf( - generateCard(CommunalGridLayoutCard.Size.FULL), - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.THIRD), - generateCard(CommunalGridLayoutCard.Size.THIRD), - generateCard(CommunalGridLayoutCard.Size.THIRD), - ) - val expected = - listOf( - listOf( - CommunalGridLayoutCard.Size.FULL, - ), - listOf( - CommunalGridLayoutCard.Size.HALF, - CommunalGridLayoutCard.Size.HALF, - ), - listOf( - CommunalGridLayoutCard.Size.THIRD, - CommunalGridLayoutCard.Size.THIRD, - CommunalGridLayoutCard.Size.THIRD, - ), - ) - - assertDistributionBySize(cards, expected) - } - - @Test - fun distribution_layoutWithGaps() { - val cards = - listOf( - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.THIRD), - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.FULL), - generateCard(CommunalGridLayoutCard.Size.THIRD), - ) - val expected = - listOf( - listOf( - CommunalGridLayoutCard.Size.HALF, - CommunalGridLayoutCard.Size.THIRD, - ), - listOf( - CommunalGridLayoutCard.Size.HALF, - ), - listOf( - CommunalGridLayoutCard.Size.FULL, - ), - listOf( - CommunalGridLayoutCard.Size.THIRD, - ), - ) - - assertDistributionBySize(cards, expected) - } - - @Test - fun distribution_sortByPriority() { - val cards = - listOf( - generateCard(priority = 2), - generateCard(priority = 7), - generateCard(priority = 10), - generateCard(priority = 1), - generateCard(priority = 5), - ) - val expected = - listOf( - listOf(10, 7), - listOf(5, 2), - listOf(1), - ) - - assertDistributionByPriority(cards, expected) - } - - private fun assertDistributionBySize( - cards: List<CommunalGridLayoutCard>, - expected: List<List<CommunalGridLayoutCard.Size>>, - ) { - val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards) - - for (c in expected.indices) { - for (r in expected[c].indices) { - assertThat(result[c][r].size).isEqualTo(expected[c][r]) - } - } - } - - private fun assertDistributionByPriority( - cards: List<CommunalGridLayoutCard>, - expected: List<List<Int>>, - ) { - val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards) - - for (c in expected.indices) { - for (r in expected[c].indices) { - assertThat(result[c][r].card.priority).isEqualTo(expected[c][r]) - } - } - } - - private fun generateCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard { - return object : CommunalGridLayoutCard() { - override val supportedSizes = listOf(size) - - @Composable - override fun Content(modifier: Modifier, size: SizeF) { - Card(modifier = modifier, content = {}) - } - } - } - - private fun generateCard(priority: Int): CommunalGridLayoutCard { - return object : CommunalGridLayoutCard() { - override val supportedSizes = listOf(Size.HALF) - override val priority = priority - - @Composable - override fun Content(modifier: Modifier, size: SizeF) { - Card(modifier = modifier, content = {}) - } - } - } -} diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt deleted file mode 100644 index 946eeecbec5d..000000000000 --- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.android.systemui.communal.layout.ui.compose.config - -import androidx.compose.ui.unit.dp -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.google.common.truth.Truth -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class CommunalGridLayoutConfigTest { - @Test - fun cardWidth() { - Truth.assertThat( - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 3.dp, - gridHeight = 17.dp, - gridColumnsPerCard = 1, - ) - .cardWidth - ) - .isEqualTo(5.dp) - - Truth.assertThat( - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 3.dp, - gridHeight = 17.dp, - gridColumnsPerCard = 2, - ) - .cardWidth - ) - .isEqualTo(13.dp) - - Truth.assertThat( - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 3.dp, - gridHeight = 17.dp, - gridColumnsPerCard = 3, - ) - .cardWidth - ) - .isEqualTo(21.dp) - } - - @Test - fun cardHeight() { - val config = - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 2.dp, - gridHeight = 10.dp, - gridColumnsPerCard = 3, - ) - - Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.FULL)).isEqualTo(10.dp) - Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.HALF)).isEqualTo(4.dp) - Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.THIRD)).isEqualTo(2.dp) - } -} diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp index 16c24375d14f..796abf4b52d6 100644 --- a/packages/SystemUI/compose/features/Android.bp +++ b/packages/SystemUI/compose/features/Android.bp @@ -31,7 +31,6 @@ android_library { ], static_libs: [ - "CommunalLayoutLib", "SystemUI-core", "PlatformComposeCore", "PlatformComposeSceneTransitionLayout", diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index ce84c19a53ee..2c4dc806f468 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -65,10 +65,13 @@ fun CommunalContainer( viewModel.currentScene .transform<CommunalSceneKey, SceneKey> { value -> value.toTransitionSceneKey() } .collectAsState(TransitionSceneKey.Blank) + // Don't show hub mode UI if keyguard is present. This is important since we're in the shade, + // which can be opened from many locations. + val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false) // Failsafe to hide the whole SceneTransitionLayout in case of bugginess. var showSceneTransitionLayout by remember { mutableStateOf(true) } - if (!showSceneTransitionLayout) { + if (!showSceneTransitionLayout || !isKeyguardShowing) { return } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index 28a4801d582a..765468372604 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -25,9 +25,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -36,7 +33,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -76,9 +72,6 @@ fun SceneScope.QuickSettings( .element(QuickSettings.Elements.Content) .fillMaxWidth() .defaultMinSize(minHeight = 300.dp) - .clip(RoundedCornerShape(32.dp)) - .background(MaterialTheme.colorScheme.primary) - .padding(1.dp), ) { QuickSettingsContent(qsSceneAdapter = qsSceneAdapter, state) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 9dd7bfaf3549..871d9f9b7627 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -23,7 +23,7 @@ import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically -import androidx.compose.foundation.clickable +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -51,6 +52,7 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.ui.composable.ComposableScene import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.ExpandedShadeHeader +import com.android.systemui.shade.ui.composable.Shade import com.android.systemui.shade.ui.composable.ShadeHeader import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager @@ -104,53 +106,59 @@ private fun SceneScope.QuickSettingsScene( ) { // TODO(b/280887232): implement the real UI. Box(modifier = modifier.fillMaxSize()) { - val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState() - val collapsedHeaderHeight = - with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() } - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = - Modifier.fillMaxSize() - .clickable(onClick = { viewModel.onContentClicked() }) - .padding(start = 16.dp, end = 16.dp, bottom = 48.dp) - ) { - when (LocalWindowSizeClass.current.widthSizeClass) { - WindowWidthSizeClass.Compact -> - AnimatedVisibility( - visible = !isCustomizing, - enter = - expandVertically( - animationSpec = tween(1000), - initialHeight = { collapsedHeaderHeight }, - ) + fadeIn(tween(1000)), - exit = - shrinkVertically( - animationSpec = tween(1000), - targetHeight = { collapsedHeaderHeight }, - shrinkTowards = Alignment.Top, - ) + fadeOut(tween(1000)), - ) { - ExpandedShadeHeader( + Box(modifier = Modifier.fillMaxSize()) { + val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState() + val collapsedHeaderHeight = + with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() } + Spacer( + modifier = + Modifier.element(Shade.Elements.ScrimBackground) + .fillMaxSize() + .background(MaterialTheme.colorScheme.scrim, shape = Shade.Shapes.Scrim) + ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = + Modifier.fillMaxSize().padding(start = 16.dp, end = 16.dp, bottom = 48.dp) + ) { + when (LocalWindowSizeClass.current.widthSizeClass) { + WindowWidthSizeClass.Compact -> + AnimatedVisibility( + visible = !isCustomizing, + enter = + expandVertically( + animationSpec = tween(1000), + initialHeight = { collapsedHeaderHeight }, + ) + fadeIn(tween(1000)), + exit = + shrinkVertically( + animationSpec = tween(1000), + targetHeight = { collapsedHeaderHeight }, + shrinkTowards = Alignment.Top, + ) + fadeOut(tween(1000)), + ) { + ExpandedShadeHeader( + viewModel = viewModel.shadeHeaderViewModel, + createTintedIconManager = createTintedIconManager, + createBatteryMeterViewController = createBatteryMeterViewController, + statusBarIconController = statusBarIconController, + ) + } + else -> + CollapsedShadeHeader( viewModel = viewModel.shadeHeaderViewModel, createTintedIconManager = createTintedIconManager, createBatteryMeterViewController = createBatteryMeterViewController, statusBarIconController = statusBarIconController, ) - } - else -> - CollapsedShadeHeader( - viewModel = viewModel.shadeHeaderViewModel, - createTintedIconManager = createTintedIconManager, - createBatteryMeterViewController = createBatteryMeterViewController, - statusBarIconController = statusBarIconController, - ) + } + Spacer(modifier = Modifier.height(16.dp)) + QuickSettings( + modifier = Modifier.fillMaxHeight(), + viewModel.qsSceneAdapter, + QSSceneAdapter.State.QS + ) } - Spacer(modifier = Modifier.height(16.dp)) - QuickSettings( - modifier = Modifier.fillMaxHeight(), - viewModel.qsSceneAdapter, - QSSceneAdapter.State.QS - ) } HeadsUpNotificationSpace( viewModel = viewModel.notifications, diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index f0e3c99b007d..643420989d1a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -133,16 +133,6 @@ public interface ActivityStarter { boolean afterKeyguardGone, boolean deferred); - /** Execute a runnable after dismissing keyguard. */ - void executeRunnableDismissingKeyguard( - Runnable runnable, - Runnable cancelAction, - boolean dismissShade, - boolean afterKeyguardGone, - boolean deferred, - boolean willAnimateOnKeyguard, - @Nullable String customMessage); - /** Whether we should animate an activity launch. */ boolean shouldAnimateLaunch(boolean isActivityIntent); diff --git a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml index 952f056b3023..cc99f5e125f3 100644 --- a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml +++ b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml @@ -22,13 +22,15 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" - android:orientation="horizontal" > + android:orientation="horizontal" + android:theme="@style/Theme.SystemUI.QuickSettings.Header" > <com.android.systemui.util.AutoMarqueeTextView android:id="@+id/mobile_carrier_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" + android:textAppearance="@style/TextAppearance.QS.Status.Carriers" android:layout_marginEnd="@dimen/qs_carrier_margin_width" android:visibility="gone" android:textDirection="locale" diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml index 355e75d0716b..9c08f5ef4cfe 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg.xml @@ -18,7 +18,7 @@ <layer-list xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:attr/colorControlHighlight"> - <item> + <item android:id="@+id/notification_background_color_layer"> <shape> <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" /> </shape> diff --git a/packages/SystemUI/res/layout/udfps_touch_overlay.xml b/packages/SystemUI/res/layout/udfps_touch_overlay.xml new file mode 100644 index 000000000000..ea92776aba2d --- /dev/null +++ b/packages/SystemUI/res/layout/udfps_touch_overlay.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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.biometrics.ui.view.UdfpsTouchOverlay xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/udfps_touch_overlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/accessibility_fingerprint_label"> +</com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay> diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt index 375727437b8b..1ee58deb501c 100644 --- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt +++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt @@ -317,7 +317,7 @@ class ActiveUnlockConfig @Inject constructor( } keyguardUpdateMonitor?.let { - val anyFaceEnrolled = it.isFaceEnrolled + val anyFaceEnrolled = it.isFaceEnabledAndEnrolled val anyFingerprintEnrolled = it.isUnlockWithFingerprintPossible( selectedUserInteractor.getSelectedUserId()) val udfpsEnrolled = it.isUdfpsEnrolled @@ -372,7 +372,7 @@ class ActiveUnlockConfig @Inject constructor( keyguardUpdateMonitor?.let { pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" + "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}") - pw.println(" faceEnrolled=${it.isFaceEnrolled}") + pw.println(" isFaceEnabledAndEnrolled=${it.isFaceEnabledAndEnrolled}") pw.println(" fpUnlockPossible=${ it.isUnlockWithFingerprintPossible(selectedUserInteractor.getSelectedUserId())}") pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}") diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 1fa55f5d839b..54cb501db002 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -384,6 +384,11 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } + @Nullable + View getAodNotifIconContainer() { + return mAodIconContainer; + } + @Override protected void onViewDetached() { mClockRegistry.unregisterClockChangeListener(mClockChangedListener); @@ -639,6 +644,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } else { mNotificationIconAreaController.setupAodIcons(nic); + mAodIconContainer = nic; } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt deleted file mode 100644 index d7019b5c5d04..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt +++ /dev/null @@ -1,169 +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 - -import android.annotation.CurrentTimeMillisLong -import com.android.systemui.common.buffer.RingBuffer -import com.android.systemui.dump.DumpsysTableLogger -import com.android.systemui.dump.Row - -/** Verbose debug information associated. */ -data class KeyguardFaceListenModel( - @CurrentTimeMillisLong override var timeMillis: Long = 0L, - override var userId: Int = 0, - override var listening: Boolean = false, - // keep sorted - var allowedDisplayStateWhileAwake: Boolean = false, - var alternateBouncerShowing: Boolean = false, - var authInterruptActive: Boolean = false, - var biometricSettingEnabledForUser: Boolean = false, - var bouncerFullyShown: Boolean = false, - var faceAndFpNotAuthenticated: Boolean = false, - var faceAuthAllowed: Boolean = false, - var faceDisabled: Boolean = false, - var faceLockedOut: Boolean = false, - var goingToSleep: Boolean = false, - var keyguardAwake: Boolean = false, - var keyguardGoingAway: Boolean = false, - var listeningForFaceAssistant: Boolean = false, - var occludingAppRequestingFaceAuth: Boolean = false, - var postureAllowsListening: Boolean = false, - var secureCameraLaunched: Boolean = false, - var supportsDetect: Boolean = false, - var switchingUser: Boolean = false, - var systemUser: Boolean = false, - var udfpsFingerDown: Boolean = false, - var userNotTrustedOrDetectionIsNeeded: Boolean = false, -) : KeyguardListenModel() { - - /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */ - val asStringList: List<String> by lazy { - listOf( - DATE_FORMAT.format(timeMillis), - timeMillis.toString(), - userId.toString(), - listening.toString(), - // keep sorted - allowedDisplayStateWhileAwake.toString(), - alternateBouncerShowing.toString(), - authInterruptActive.toString(), - biometricSettingEnabledForUser.toString(), - bouncerFullyShown.toString(), - faceAndFpNotAuthenticated.toString(), - faceAuthAllowed.toString(), - faceDisabled.toString(), - faceLockedOut.toString(), - goingToSleep.toString(), - keyguardAwake.toString(), - keyguardGoingAway.toString(), - listeningForFaceAssistant.toString(), - occludingAppRequestingFaceAuth.toString(), - postureAllowsListening.toString(), - secureCameraLaunched.toString(), - supportsDetect.toString(), - switchingUser.toString(), - systemUser.toString(), - udfpsFingerDown.toString(), - userNotTrustedOrDetectionIsNeeded.toString(), - ) - } - - /** - * [RingBuffer] to store [KeyguardFaceListenModel]. After the buffer is full, it will recycle - * old events. - * - * Do not use [append] to add new elements. Instead use [insert], as it will recycle if - * necessary. - */ - class Buffer { - private val buffer = RingBuffer(CAPACITY) { KeyguardFaceListenModel() } - - fun insert(model: KeyguardFaceListenModel) { - buffer.advance().apply { - timeMillis = model.timeMillis - userId = model.userId - listening = model.listening - // keep sorted - allowedDisplayStateWhileAwake = model.allowedDisplayStateWhileAwake - alternateBouncerShowing = model.alternateBouncerShowing - authInterruptActive = model.authInterruptActive - biometricSettingEnabledForUser = model.biometricSettingEnabledForUser - bouncerFullyShown = model.bouncerFullyShown - faceAndFpNotAuthenticated = model.faceAndFpNotAuthenticated - faceAuthAllowed = model.faceAuthAllowed - faceDisabled = model.faceDisabled - faceLockedOut = model.faceLockedOut - goingToSleep = model.goingToSleep - keyguardAwake = model.keyguardAwake - keyguardGoingAway = model.keyguardGoingAway - listeningForFaceAssistant = model.listeningForFaceAssistant - occludingAppRequestingFaceAuth = model.occludingAppRequestingFaceAuth - postureAllowsListening = model.postureAllowsListening - secureCameraLaunched = model.secureCameraLaunched - supportsDetect = model.supportsDetect - switchingUser = model.switchingUser - systemUser = model.systemUser - udfpsFingerDown = model.udfpsFingerDown - userNotTrustedOrDetectionIsNeeded = model.userNotTrustedOrDetectionIsNeeded - } - } - /** - * Returns the content of the buffer (sorted from latest to newest). - * - * @see KeyguardFingerprintListenModel.asStringList - */ - fun toList(): List<Row> { - return buffer.asSequence().map { it.asStringList }.toList() - } - } - - companion object { - const val CAPACITY = 40 // number of logs to retain - - /** Headers for dumping a table using [DumpsysTableLogger]. */ - @JvmField - val TABLE_HEADERS = - listOf( - "timestamp", - "time_millis", - "userId", - "listening", - // keep sorted - "allowedDisplayStateWhileAwake", - "alternateBouncerShowing", - "authInterruptActive", - "biometricSettingEnabledForUser", - "bouncerFullyShown", - "faceAndFpNotAuthenticated", - "faceAuthAllowed", - "faceDisabled", - "faceLockedOut", - "goingToSleep", - "keyguardAwake", - "keyguardGoingAway", - "listeningForFaceAssistant", - "occludingAppRequestingFaceAuth", - "postureAllowsListening", - "secureCameraLaunched", - "supportsDetect", - "switchingUser", - "systemUser", - "udfpsFingerDown", - "userNotTrustedOrDetectionIsNeeded", - ) - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 1b6112f52082..f706301df1ca 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -214,7 +214,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard public void onUserInput() { mBouncerMessageInteractor.onPrimaryBouncerUserInput(); mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput(); - mUpdateMonitor.cancelFaceAuth(); } @Override @@ -340,16 +339,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final SwipeListener mSwipeListener = new SwipeListener() { @Override public void onSwipeUp() { - if (!mUpdateMonitor.isFaceDetectionRunning()) { - mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer(); - boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth( - FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER); + if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) { mKeyguardSecurityCallback.userActivity(); - if (didFaceAuthRun) { - showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true); - } } - if (mUpdateMonitor.isFaceEnrolled()) { + mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer(); + if (mKeyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) { mUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT, "swipeUpOnBouncer"); @@ -755,7 +749,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } mView.onResume( mSecurityModel.getSecurityMode(mSelectedUserInteractor.getSelectedUserId()), - mKeyguardStateController.isFaceEnrolled()); + mKeyguardStateController.isFaceEnrolledAndEnabled()); } /** Sets an initial message that would override the default message */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 87d937bc45fb..4fbf077a8852 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -84,6 +84,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV Dumpable { private static final boolean DEBUG = KeyguardConstants.DEBUG; @VisibleForTesting static final String TAG = "KeyguardStatusViewController"; + private static final long STATUS_AREA_HEIGHT_ANIMATION_MILLIS = 133; /** * Duration to use for the animator when the keyguard status view alignment changes, and a @@ -104,6 +105,10 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final KeyguardInteractor mKeyguardInteractor; private final PowerInteractor mPowerInteractor; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final DozeParameters mDozeParameters; + + private View mStatusArea = null; + private ValueAnimator mStatusAreaHeightAnimator = null; private Boolean mSplitShadeEnabled = false; private Boolean mStatusViewCentered = true; @@ -123,6 +128,46 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } }; + private final View.OnLayoutChangeListener mStatusAreaLayoutChangeListener = + new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, + int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom + ) { + if (!mDozeParameters.getAlwaysOn()) { + return; + } + + int oldHeight = oldBottom - oldTop; + int diff = v.getHeight() - oldHeight; + if (diff == 0) { + return; + } + + int startValue = -1 * diff; + long duration = STATUS_AREA_HEIGHT_ANIMATION_MILLIS; + if (mStatusAreaHeightAnimator != null + && mStatusAreaHeightAnimator.isRunning()) { + duration += mStatusAreaHeightAnimator.getDuration() + - mStatusAreaHeightAnimator.getCurrentPlayTime(); + startValue += (int) mStatusAreaHeightAnimator.getAnimatedValue(); + mStatusAreaHeightAnimator.cancel(); + mStatusAreaHeightAnimator = null; + } + + mStatusAreaHeightAnimator = ValueAnimator.ofInt(startValue, 0); + mStatusAreaHeightAnimator.setDuration(duration); + final View nic = mKeyguardClockSwitchController.getAodNotifIconContainer(); + if (nic != null) { + mStatusAreaHeightAnimator.addUpdateListener(anim -> { + nic.setTranslationY((int) anim.getAnimatedValue()); + }); + } + mStatusAreaHeightAnimator.start(); + } + }; + @Inject public KeyguardStatusViewController( KeyguardStatusView keyguardStatusView, @@ -144,6 +189,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mKeyguardClockSwitchController = keyguardClockSwitchController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mConfigurationController = configurationController; + mDozeParameters = dozeParameters; mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters, screenOffAnimationController, /* animateYPos= */ true, logger.getBuffer()); @@ -218,12 +264,15 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV @Override protected void onViewAttached() { + mStatusArea = mView.findViewById(R.id.keyguard_status_area); + mStatusArea.addOnLayoutChangeListener(mStatusAreaLayoutChangeListener); mKeyguardUpdateMonitor.registerCallback(mInfoCallback); mConfigurationController.addCallback(mConfigurationListener); } @Override protected void onViewDetached() { + mStatusArea.removeOnLayoutChangeListener(mStatusAreaLayoutChangeListener); mKeyguardUpdateMonitor.removeCallback(mInfoCallback); mConfigurationController.removeCallback(mConfigurationListener); } @@ -293,9 +342,15 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV /** * Get the height of the keyguard status view without the notification icon area, as that's * only visible on AOD. + * + * We internally animate height changes to the status area to prevent discontinuities in the + * doze animation introduced by the height suddenly changing due to smartpace. */ public int getLockscreenHeight() { - return mView.getHeight() - mKeyguardClockSwitchController.getNotificationIconAreaHeight(); + int heightAnimValue = mStatusAreaHeightAnimator == null ? 0 : + (int) mStatusAreaHeightAnimator.getAnimatedValue(); + return mView.getHeight() + heightAnimValue + - mKeyguardClockSwitchController.getNotificationIconAreaHeight(); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index baab637a979c..c5bb0995f492 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -34,48 +34,11 @@ import static android.hardware.biometrics.BiometricSourceType.FACE; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT; -import static android.os.PowerManager.WAKE_REASON_UNKNOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; -import static com.android.keyguard.FaceAuthReasonKt.apiRequestReasonToUiEvent; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_DISPLAY_OFF; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_DREAM_STARTED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FP_LOCKED_OUT; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_TRUST_ENABLED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_USER_INPUT_ON_BOUNCER; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALL_AUTHENTICATORS_REGISTERED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_DURING_CANCELLATION; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_TRUST_DISABLED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_CAMERA_LAUNCHED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_FP_AUTHENTICATED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_GOING_TO_SLEEP; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_OCCLUSION_CHANGED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_RESET; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_POSTURE_CHANGED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED; -import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING; -import static com.android.systemui.DejankUtils.whitelistIpcs; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN; @@ -104,10 +67,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.SensorProperties; import android.hardware.biometrics.SensorPropertiesInternal; -import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceManager; -import android.hardware.face.FaceSensorPropertiesInternal; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; @@ -137,7 +97,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.view.Display; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -161,7 +120,6 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; -import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent; @@ -172,12 +130,10 @@ import com.android.systemui.keyguard.shared.model.FaceDetectionStatus; import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus; import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus; import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus; -import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.WeatherData; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; -import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; @@ -318,7 +274,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final boolean mIsSystemUser; private final AuthController mAuthController; private final UiEventLogger mUiEventLogger; - private final Set<Integer> mFaceAcquiredInfoIgnoreList; private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage; private final PackageManager mPackageManager; private int mStatusBarState; @@ -339,26 +294,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } }; - private final DisplayTracker.Callback mDisplayCallback = new DisplayTracker.Callback() { - @Override - public void onDisplayChanged(int displayId) { - if (displayId != Display.DEFAULT_DISPLAY) { - return; - } - - if (mWakefulness.getWakefulness() == WAKEFULNESS_AWAKE - && mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState() - == Display.STATE_OFF) { - mAllowedDisplayStateWhileAwakeForFaceAuth = false; - updateFaceListeningState( - BIOMETRIC_ACTION_STOP, - FACE_AUTH_DISPLAY_OFF - ); - } else { - mAllowedDisplayStateWhileAwakeForFaceAuth = true; - } - } - }; private final FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig; HashMap<Integer, SimData> mSimDatas = new HashMap<>(); @@ -377,9 +312,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean mNeedsSlowUnlockTransition; private boolean mAssistantVisible; private boolean mOccludingAppRequestingFp; - private boolean mOccludingAppRequestingFace; private boolean mSecureCameraLaunched; - private boolean mAllowedDisplayStateWhileAwakeForFaceAuth = true; private boolean mBiometricPromptShowing; @VisibleForTesting protected boolean mTelephonyCapable; @@ -409,7 +342,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final TrustManager mTrustManager; private final UserManager mUserManager; private final DevicePolicyManager mDevicePolicyManager; - private final DevicePostureController mPostureController; private final BroadcastDispatcher mBroadcastDispatcher; private final InteractionJankMonitor mInteractionJankMonitor; private final LatencyTracker mLatencyTracker; @@ -422,13 +354,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Nullable private final FingerprintManager mFpm; @Nullable - private final FaceManager mFaceManager; - @Nullable private KeyguardFaceAuthInteractor mFaceAuthInteractor; private final TaskStackChangeListeners mTaskStackChangeListeners; private final IActivityTaskManager mActivityTaskManager; - private final WakefulnessLifecycle mWakefulness; - private final DisplayTracker mDisplayTracker; private final SelectedUserInteractor mSelectedUserInteractor; private final LockPatternUtils mLockPatternUtils; @VisibleForTesting @@ -439,11 +367,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private List<SubscriptionInfo> mSubscriptionInfo; @VisibleForTesting protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; - private int mFaceRunningState = BIOMETRIC_STATE_STOPPED; private boolean mIsDreaming; private boolean mLogoutEnabled; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private int mPostureState = DEVICE_POSTURE_UNKNOWN; private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider; /** @@ -455,7 +381,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // If the HAL dies or is unable to authenticate, keyguard should retry after a short delay private int mHardwareFingerprintUnavailableRetryCount = 0; - private int mHardwareFaceUnavailableRetryCount = 0; private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms private static final int HAL_ERROR_RETRY_MAX = 20; @@ -465,7 +390,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived; - private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived; private final Provider<SessionTracker> mSessionTrackerProvider; @VisibleForTesting @@ -481,8 +405,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public void onChanged(boolean enabled, int userId) { mHandler.post(() -> { mBiometricEnabledForUser.put(userId, enabled); - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); }); } }; @@ -525,15 +448,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final KeyguardFingerprintListenModel.Buffer mFingerprintListenBuffer = new KeyguardFingerprintListenModel.Buffer(); - private final KeyguardFaceListenModel.Buffer mFaceListenBuffer = - new KeyguardFaceListenModel.Buffer(); private final KeyguardActiveUnlockModel.Buffer mActiveUnlockTriggerBuffer = new KeyguardActiveUnlockModel.Buffer(); @VisibleForTesting SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>(); - @VisibleForTesting - SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); private static int sCurrentUser; @@ -561,11 +480,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // authenticating. TrustManager sends an onTrustChanged whenever a user unlocks keyguard, // for this reason we need to make sure to not authenticate. if (wasTrusted == enabled || enabled) { - updateBiometricListeningState(BIOMETRIC_ACTION_STOP, - FACE_AUTH_STOPPED_TRUST_ENABLED); + updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); } else { - updateBiometricListeningState(BIOMETRIC_ACTION_START, - FACE_AUTH_TRIGGERED_TRUST_DISABLED); + updateFingerprintListeningState(BIOMETRIC_ACTION_START); } mLogger.logTrustChanged(wasTrusted, enabled, userId); @@ -807,8 +724,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public void setKeyguardGoingAway(boolean goingAway) { mKeyguardGoingAway = goingAway; if (mKeyguardGoingAway) { - updateFaceListeningState(BIOMETRIC_ACTION_STOP, - FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -855,29 +770,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - if (occlusionChanged) { - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_KEYGUARD_OCCLUSION_CHANGED); - } else if (showingChanged) { - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED); + if (occlusionChanged || showingChanged) { + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } } /** - * Request to listen for face authentication when an app is occluding keyguard. - * - * @param request if true and mKeyguardOccluded, request face auth listening, else default - * to normal behavior. - * See {@link KeyguardUpdateMonitor#shouldListenForFace()} - */ - public void requestFaceAuthOnOccludingApp(boolean request) { - mOccludingAppRequestingFace = request; - int action = mOccludingAppRequestingFace ? BIOMETRIC_ACTION_UPDATE : BIOMETRIC_ACTION_STOP; - updateFaceListeningState(action, FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED); - } - - /** * Request to listen for fingerprint when an app is occluding keyguard. * * @param request if true and mKeyguardOccluded, request fingerprint listening, else default @@ -894,8 +792,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ public void onCameraLaunched() { mSecureCameraLaunched = true; - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_CAMERA_LAUNCHED); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } /** @@ -951,8 +848,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Don't send cancel if authentication succeeds mFingerprintCancelSignal = null; mLogger.logFingerprintSuccess(userId, isStrongBiometric); - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_FP_AUTHENTICATED); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -1025,7 +921,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.logFingerprintDetected(authUserId, isStrongBiometric); } else if (biometricSourceType == FACE) { mLogger.logFaceDetected(authUserId, isStrongBiometric); - setFaceRunningState(BIOMETRIC_STATE_STOPPED); } Trace.endSection(); @@ -1140,7 +1035,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (isUdfpsEnrolled()) { updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } - stopListeningForFace(FACE_AUTH_STOPPED_FP_LOCKED_OUT); } mLogger.logFingerprintError(msgId, errString); @@ -1218,16 +1112,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public void onFaceAuthenticated(int userId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated"); Assert.isMainThread(); - mUserFaceAuthenticated.put(userId, - new BiometricAuthenticated(true, isStrongBiometric)); // Update/refresh trust state only if user can skip bouncer if (getUserCanSkipBouncer(userId)) { mTrustManager.unlockedByBiometricForUser(userId, FACE); } - // Don't send cancel if authentication succeeds - mFaceCancelSignal = null; - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); mLogger.d("onFaceAuthenticated"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -1247,11 +1136,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Trace.endSection(); } - /** - * @deprecated This is being migrated to use modern architecture, this method is visible purely - * for bridging the gap while the migration is active. - */ - @Deprecated private void handleFaceAuthFailed() { Assert.isMainThread(); String reason = @@ -1264,8 +1148,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab "faceFailure-" + reason); mLogger.d("onFaceAuthFailed"); - mFaceCancelSignal = null; - setFaceRunningState(BIOMETRIC_STATE_STOPPED); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -1276,11 +1158,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mContext.getString(R.string.kg_face_not_recognized)); } - /** - * @deprecated This is being migrated to use modern architecture, this method is visible purely - * for bridging the gap while the migration is active. - */ - @Deprecated private void handleFaceAcquired(int acquireInfo) { Assert.isMainThread(); for (int i = 0; i < mCallbacks.size(); i++) { @@ -1298,44 +1175,23 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - /** - * @deprecated This is being migrated to use modern architecture, this method is visible purely - * for bridging the gap while the migration is active. - */ - @Deprecated private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated"); - try { - if (mGoingToSleep) { - mLogger.d("Aborted successful auth because device is going to sleep."); - return; - } - final int userId = mSelectedUserInteractor.getSelectedUserId(true); - if (userId != authUserId) { - mLogger.logFaceAuthForWrongUser(authUserId); - return; - } - if (!isFaceAuthInteractorEnabled() && isFaceDisabled(userId)) { - mLogger.logFaceAuthDisabledForUser(userId); - return; - } - mLogger.logFaceAuthSuccess(userId); - onFaceAuthenticated(userId, isStrongBiometric); - } finally { - setFaceRunningState(BIOMETRIC_STATE_STOPPED); + if (mGoingToSleep) { + mLogger.d("Aborted successful auth because device is going to sleep."); + return; } + final int userId = mSelectedUserInteractor.getSelectedUserId(true); + if (userId != authUserId) { + mLogger.logFaceAuthForWrongUser(authUserId); + return; + } + mLogger.logFaceAuthSuccess(userId); + onFaceAuthenticated(userId, isStrongBiometric); Trace.endSection(); } - /** - * @deprecated This is being migrated to use modern architecture, this method is visible purely - * for bridging the gap while the migration is active. - */ - @Deprecated private void handleFaceHelp(int msgId, String helpString) { - if (mFaceAcquiredInfoIgnoreList.contains(msgId)) { - return; - } Assert.isMainThread(); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -1345,49 +1201,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - /** - * @deprecated This is being migrated to use modern architecture, this method is visible purely - * for bridging the gap while the migration is active. - */ - @Deprecated private void handleFaceError(int msgId, final String originalErrMsg) { Assert.isMainThread(); String errString = originalErrMsg; mLogger.logFaceAuthError(msgId, originalErrMsg); - if (mHandler.hasCallbacks(mFaceCancelNotReceived)) { - mHandler.removeCallbacks(mFaceCancelNotReceived); - } // Error is always the end of authentication lifecycle - mFaceCancelSignal = null; boolean cameraPrivacyEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled( SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, SensorPrivacyManager.Sensors.CAMERA); - if (msgId == FaceManager.FACE_ERROR_CANCELED - && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) { - setFaceRunningState(BIOMETRIC_STATE_STOPPED); - updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_TRIGGERED_DURING_CANCELLATION); - } else { - setFaceRunningState(BIOMETRIC_STATE_STOPPED); - } - final boolean isHwUnavailable = msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE; - if (isHwUnavailable - || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) { - if (mHardwareFaceUnavailableRetryCount < HAL_ERROR_RETRY_MAX) { - mHardwareFaceUnavailableRetryCount++; - mHandler.removeCallbacks(mRetryFaceAuthentication); - mHandler.postDelayed(mRetryFaceAuthentication, HAL_ERROR_RETRY_TIMEOUT); - } - } - - boolean lockedOutStateChanged = false; if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) { - lockedOutStateChanged = !mFaceLockedOutPermanent; - mFaceLockedOutPermanent = true; - if (isFaceClass3()) { + if (getFaceAuthInteractor() != null && getFaceAuthInteractor().isFaceAuthStrong()) { updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); } } @@ -1404,10 +1230,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - if (lockedOutStateChanged) { - notifyLockedOutStateChanged(FACE); - } - if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(msgId)) { requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL, @@ -1415,49 +1237,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private final Runnable mRetryFaceAuthentication = new Runnable() { - @Override - public void run() { - mLogger.logRetryingAfterFaceHwUnavailable(mHardwareFaceUnavailableRetryCount); - updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE); - } - }; - - private void onFaceCancelNotReceived() { - mLogger.e("Face cancellation not received, transitioning to STOPPED"); - mFaceRunningState = BIOMETRIC_STATE_STOPPED; - KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP, - FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED); - } - - private void handleFaceLockoutReset(@LockoutMode int mode) { - mLogger.logFaceLockoutReset(mode); - final boolean wasLockoutPermanent = mFaceLockedOutPermanent; - mFaceLockedOutPermanent = (mode == BIOMETRIC_LOCKOUT_PERMANENT); - final boolean changed = (mFaceLockedOutPermanent != wasLockoutPermanent); - - mHandler.postDelayed(() -> updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET), getBiometricLockoutDelay()); - - if (changed) { - notifyLockedOutStateChanged(FACE); - } - } - - private void setFaceRunningState(int faceRunningState) { - boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING; - boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING; - mFaceRunningState = faceRunningState; - mLogger.logFaceRunningState(mFaceRunningState); - // Clients of KeyguardUpdateMonitor don't care about the internal state or about the - // asynchronousness of the cancel cycle. So only notify them if the actually running state - // has changed. - if (wasRunning != isRunning) { - notifyFaceRunningStateChanged(); - } - } - private void notifyFaceRunningStateChanged() { Assert.isMainThread(); for (int i = 0; i < mCallbacks.size(); i++) { @@ -1478,14 +1257,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ @Deprecated public boolean isFaceDetectionRunning() { - if (isFaceAuthInteractorEnabled()) { - return getFaceAuthInteractor().isRunning(); - } - return mFaceRunningState == BIOMETRIC_STATE_RUNNING; - } - - private boolean isFaceAuthInteractorEnabled() { - return mFaceAuthInteractor != null && mFaceAuthInteractor.isEnabled(); + return getFaceAuthInteractor() != null && getFaceAuthInteractor().isRunning(); } private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() { @@ -1495,14 +1267,32 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * Set the face auth interactor that should be used for initiating face authentication. */ - public void setFaceAuthInteractor(@Nullable KeyguardFaceAuthInteractor faceAuthInteractor) { + public void setFaceAuthInteractor(KeyguardFaceAuthInteractor faceAuthInteractor) { + if (mFaceAuthInteractor != null) { + mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener); + } mFaceAuthInteractor = faceAuthInteractor; mFaceAuthInteractor.registerListener(mFaceAuthenticationListener); } - private FaceAuthenticationListener mFaceAuthenticationListener = + private final FaceAuthenticationListener mFaceAuthenticationListener = new FaceAuthenticationListener() { @Override + public void onAuthEnrollmentStateChanged(boolean enrolled) { + notifyAboutEnrollmentChange(TYPE_FACE); + } + + @Override + public void onRunningStateChanged(boolean isRunning) { + notifyFaceRunningStateChanged(); + } + + @Override + public void onLockoutStateChanged(boolean isLockedOut) { + notifyLockedOutStateChanged(FACE); + } + + @Override public void onAuthenticationStatusChanged( @NonNull FaceAuthenticationStatus status ) { @@ -1546,32 +1336,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** - * @deprecated This method is not needed anymore with the new face auth system. - */ - @Deprecated - private boolean isFaceDisabled(int userId) { - // TODO(b/140035044) - return whitelistIpcs(() -> - (mDevicePolicyManager.getKeyguardDisabledFeatures(null, userId) - & DevicePolicyManager.KEYGUARD_DISABLE_FACE) != 0 - || isSimPinSecure()); - } - - /** * @return whether the current user has been authenticated with face. This may be true * on the lockscreen if the user doesn't have bypass enabled. * - * @deprecated This is being migrated to use modern architecture. + * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} */ @Deprecated public boolean getIsFaceAuthenticated() { - boolean faceAuthenticated = false; - BiometricAuthenticated bioFaceAuthenticated = - mUserFaceAuthenticated.get(mSelectedUserInteractor.getSelectedUserId()); - if (bioFaceAuthenticated != null) { - faceAuthenticated = bioFaceAuthenticated.mAuthenticated; - } - return faceAuthenticated; + return getFaceAuthInteractor() != null && getFaceAuthInteractor().isAuthenticated(); } public boolean getUserCanSkipBouncer(int userId) { @@ -1590,17 +1362,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId); boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric); - return fingerprintAllowed || getUserUnlockedWithFace(userId); + boolean unlockedByFace = isCurrentUserUnlockedWithFace() && isUnlockingWithBiometricAllowed( + FACE); + return fingerprintAllowed || unlockedByFace; } /** * Returns whether the user is unlocked with face. + * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} instead */ - public boolean getUserUnlockedWithFace(int userId) { - BiometricAuthenticated face = mUserFaceAuthenticated.get(userId); - return face != null && face.mAuthenticated - && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric); + @Deprecated + public boolean isCurrentUserUnlockedWithFace() { + return getFaceAuthInteractor() != null && getFaceAuthInteractor().isAuthenticated(); } /** @@ -1609,13 +1383,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ public boolean getUserUnlockedWithBiometricAndIsBypassing(int userId) { BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId); - BiometricAuthenticated face = mUserFaceAuthenticated.get(userId); // fingerprint always bypasses boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric); - boolean faceAllowed = face != null && face.mAuthenticated - && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric); - return fingerprintAllowed || faceAllowed && mKeyguardBypassController.canBypass(); + return fingerprintAllowed || (isCurrentUserUnlockedWithFace() + && mKeyguardBypassController.canBypass()); } public boolean getUserTrustIsManaged(int userId) { @@ -1684,10 +1456,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent; // however the strong auth tracker does not include the temporary lockout // mFingerprintLockedOut. + if (!mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric)) { + return false; + } + boolean isFaceLockedOut = + getFaceAuthInteractor() != null && getFaceAuthInteractor().isLockedOut(); + boolean isFaceAuthStrong = + getFaceAuthInteractor() != null && getFaceAuthInteractor().isFaceAuthStrong(); + boolean isFingerprintLockedOut = isFingerprintLockedOut(); + boolean isAnyStrongBiometricLockedOut = + (isFingerprintClass3() && isFingerprintLockedOut) || (isFaceAuthStrong + && isFaceLockedOut); // Class 3 biometric lockout will lockout ALL biometrics - return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric) - && (!isFingerprintClass3() || !isFingerprintLockedOut()) - && (!isFaceClass3() || !mFaceLockedOutPermanent); + if (isAnyStrongBiometricLockedOut) { + return false; + } + return !isFaceLockedOut || !isFingerprintLockedOut; } /** @@ -1707,7 +1491,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab case FINGERPRINT: return isUnlockingWithBiometricAllowed(isFingerprintClass3()); case FACE: - return isUnlockingWithBiometricAllowed(isFaceClass3()); + return getFaceAuthInteractor() != null + && isUnlockingWithBiometricAllowed( + getFaceAuthInteractor().isFaceAuthStrong()); default: return false; } @@ -1757,14 +1543,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } if (userId == mSelectedUserInteractor.getSelectedUserId()) { - FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED.setExtraInfo( - mStrongAuthTracker.getStrongAuthForUser( - mSelectedUserInteractor.getSelectedUserId())); - // Strong auth is only reset when primary auth is used to enter the device, // so we only check whether to stop biometric listening states here - updateBiometricListeningState( - BIOMETRIC_ACTION_STOP, FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED); + updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); } } @@ -1787,14 +1568,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } if (userId == mSelectedUserInteractor.getSelectedUserId()) { - FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED.setExtraInfo( - mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout( - mSelectedUserInteractor.getSelectedUserId()) ? -1 : 1); - // This is only reset when primary auth is used to enter the device, so we only check // whether to stop biometric listening states here - updateBiometricListeningState(BIOMETRIC_ACTION_STOP, - FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED); + updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); } } @@ -1813,11 +1589,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab void setAssistantVisible(boolean assistantVisible) { mAssistantVisible = assistantVisible; mLogger.logAssistantVisible(mAssistantVisible); - if (isFaceAuthInteractorEnabled()) { - mFaceAuthInteractor.onAssistantTriggeredOnLockScreen(); + if (getFaceAuthInteractor() != null) { + getFaceAuthInteractor().onAssistantTriggeredOnLockScreen(); } - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); if (mAssistantVisible) { requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.ASSISTANT, @@ -1929,14 +1704,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; - private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback - = new FaceManager.LockoutResetCallback() { - @Override - public void onLockoutReset(int sensorId) { - handleFaceLockoutReset(BIOMETRIC_LOCKOUT_NONE); - } - }; - /** * Propagates a pointer down event to keyguard. */ @@ -1998,7 +1765,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onUdfpsPointerDown(int sensorId) { mLogger.logUdfpsPointerDown(sensorId); - requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); } /** @@ -2024,56 +1790,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; - private final FaceManager.FaceDetectionCallback mFaceDetectionCallback - = (sensorId, userId, isStrongBiometric) -> { - // Trigger the face detected path so the bouncer can be shown - handleBiometricDetected(userId, FACE, isStrongBiometric); - }; - - @VisibleForTesting - final FaceManager.AuthenticationCallback mFaceAuthenticationCallback - = new FaceManager.AuthenticationCallback() { - - @Override - public void onAuthenticationFailed() { - handleFaceAuthFailed(); - } - - @Override - public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) { - handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric()); - } - - @Override - public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { - handleFaceHelp(helpMsgId, helpString.toString()); - } - - @Override - public void onAuthenticationError(int errMsgId, CharSequence errString) { - handleFaceError(errMsgId, errString.toString()); - } - - @Override - public void onAuthenticationAcquired(int acquireInfo) { - handleFaceAcquired(acquireInfo); - } - }; - @VisibleForTesting final DevicePostureController.Callback mPostureCallback = new DevicePostureController.Callback() { @Override public void onPostureChanged(@DevicePostureInt int posture) { - boolean currentPostureAllowsFaceAuth = doesPostureAllowFaceAuth(mPostureState); - boolean newPostureAllowsFaceAuth = doesPostureAllowFaceAuth(posture); - mPostureState = posture; - if (currentPostureAllowsFaceAuth && !newPostureAllowsFaceAuth) { - mLogger.d("New posture does not allow face auth, stopping it"); - updateFaceListeningState(BIOMETRIC_ACTION_STOP, - FACE_AUTH_UPDATED_POSTURE_CHANGED); - } - if (mPostureState == DEVICE_POSTURE_OPENED) { + if (posture == DEVICE_POSTURE_OPENED) { mLogger.d("Posture changed to open - attempting to request active unlock"); requestActiveUnlockFromWakeReason(PowerManager.WAKE_REASON_UNFOLD_DEVICE, false); @@ -2083,14 +1805,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting CancellationSignal mFingerprintCancelSignal; - @VisibleForTesting - CancellationSignal mFaceCancelSignal; private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties = Collections.emptyList(); - private List<FaceSensorPropertiesInternal> mFaceSensorProperties = Collections.emptyList(); private boolean mFingerprintLockedOut; private boolean mFingerprintLockedOutPermanent; - private boolean mFaceLockedOutPermanent; /** * When we receive a {@link android.content.Intent#ACTION_SIM_STATE_CHANGED} broadcast, @@ -2229,15 +1947,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp"); Assert.isMainThread(); - mAllowedDisplayStateWhileAwakeForFaceAuth = true; updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); - if (mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason)) { - FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason); - updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_STARTED_WAKING_UP); - } else { - mLogger.logSkipUpdateFaceListeningOnWakeup(pmWakeReason); - } requestActiveUnlockFromWakeReason(pmWakeReason, true); for (int i = 0; i < mCallbacks.size(); i++) { @@ -2264,7 +1974,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // which results in face auth running once on AoD. mAssistantVisible = false; mLogger.d("Started going to sleep, mGoingToSleep=true, mAssistantVisible=false"); - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_GOING_TO_SLEEP); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } protected void handleFinishedGoingToSleep(int arg1) { @@ -2276,15 +1986,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onFinishedGoingToSleep(arg1); } } - updateFaceListeningState(BIOMETRIC_ACTION_STOP, - FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP); updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } private void handleScreenTurnedOff() { Assert.isMainThread(); mHardwareFingerprintUnavailableRetryCount = 0; - mHardwareFaceUnavailableRetryCount = 0; } private void handleDreamingStateChanged(int dreamStart) { @@ -2297,9 +2004,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); - if (mIsDreaming) { - updateFaceListeningState(BIOMETRIC_ACTION_STOP, FACE_AUTH_STOPPED_DREAM_STARTED); - } } private void handleUserUnlocked(int userId) { @@ -2344,7 +2048,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting void resetBiometricListeningState() { mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; - mFaceRunningState = BIOMETRIC_STATE_STOPPED; } @VisibleForTesting @@ -2376,17 +2079,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab SensorPrivacyManager sensorPrivacyManager, TelephonyManager telephonyManager, PackageManager packageManager, - @Nullable FaceManager faceManager, @Nullable FingerprintManager fingerprintManager, @Nullable BiometricManager biometricManager, FaceWakeUpTriggersConfig faceWakeUpTriggersConfig, DevicePostureController devicePostureController, Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider, TaskStackChangeListeners taskStackChangeListeners, - IActivityTaskManager activityTaskManagerService, - DisplayTracker displayTracker, - WakefulnessLifecycle wakefulness, - SelectedUserInteractor selectedUserInteractor) { + SelectedUserInteractor selectedUserInteractor, + IActivityTaskManager activityTaskManagerService) { mContext = context; mSubscriptionManager = subscriptionManager; mUserTracker = userTracker; @@ -2413,16 +2113,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mDreamManager = dreamManager; mTelephonyManager = telephonyManager; mDevicePolicyManager = devicePolicyManager; - mPostureController = devicePostureController; mPackageManager = packageManager; mFpm = fingerprintManager; - mFaceManager = faceManager; mActiveUnlockConfig.setKeyguardUpdateMonitor(this); - mFaceAcquiredInfoIgnoreList = Arrays.stream( - mContext.getResources().getIntArray( - R.array.config_face_acquire_device_entry_ignorelist)) - .boxed() - .collect(Collectors.toSet()); mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger( R.integer.config_face_auth_supported_posture); mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig; @@ -2432,9 +2125,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab .collect(Collectors.toSet()); mTaskStackChangeListeners = taskStackChangeListeners; mActivityTaskManager = activityTaskManagerService; - mWakefulness = wakefulness; - mDisplayTracker = displayTracker; - mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor); mSelectedUserInteractor = selectedUserInteractor; mHandler = new Handler(mainLooper) { @@ -2521,8 +2211,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab setAssistantVisible((boolean) msg.obj); break; case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE: - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_FP_AUTHENTICATED); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); break; case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED: updateLogoutEnabled(); @@ -2617,18 +2306,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab }); mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback); } - if (mFaceManager != null) { - mFaceManager.addAuthenticatorsRegisteredCallback( - new IFaceAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FaceSensorPropertiesInternal> sensors) throws RemoteException { - mFaceSensorProperties = sensors; - mLogger.d("FaceManager onAllAuthenticatorsRegistered"); - } - }); - mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback); - } if (biometricManager != null) { biometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback); @@ -2639,16 +2316,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAllAuthenticatorsRegistered( @BiometricAuthenticator.Modality int modality) { - mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_TRIGGERED_ALL_AUTHENTICATORS_REGISTERED)); + mainExecutor.execute( + () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE)); } @Override public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { mHandler.obtainMessage(MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED, modality, 0) .sendToTarget(); - mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED)); + mainExecutor.execute( + () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE)); } @Override @@ -2665,9 +2342,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }); if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) { - mPostureController.addCallback(mPostureCallback); + devicePostureController.addCallback(mPostureCallback); } - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener); mIsSystemUser = mUserManager.isSystemUser(); @@ -2721,10 +2398,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private boolean isFaceSupported() { - return mFaceManager != null && !mFaceSensorProperties.isEmpty(); - } - private boolean isFingerprintSupported() { return mFpm != null && !mFingerprintSensorProperties.isEmpty(); } @@ -2760,17 +2433,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** - * @return true if there's at least one face enrolled for the given user. - */ - public boolean isFaceEnrolled(int userId) { - return mAuthController.isFaceAuthEnrolled(userId); - } - - /** * @return true if there's at least one face enrolled + * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()} */ - public boolean isFaceEnrolled() { - return isFaceEnrolled(mSelectedUserInteractor.getSelectedUserId()); + @Deprecated + public boolean isFaceEnabledAndEnrolled() { + return getFaceAuthInteractor() != null + && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled(); } private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @@ -2798,12 +2467,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED); } - private void updateBiometricListeningState(int action, - @NonNull FaceAuthUiEvent faceAuthUiEvent) { - updateFingerprintListeningState(action); - updateFaceListeningState(action, faceAuthUiEvent); - } - private void updateFingerprintListeningState(int action) { // If this message exists, we should not authenticate again until this message is // consumed by the handler @@ -2859,57 +2522,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return; } mAuthInterruptActive = active; - updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD); requestActiveUnlock(ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, "onReach"); } - /** - * Requests face authentication if we're on a state where it's allowed. - * This will re-trigger auth in case it fails. - * @param reason One of the reasons {@link FaceAuthApiRequestReason} on why this API is being - * invoked. - * @return current face auth detection state, true if it is running. - * @deprecated This is being migrated to use modern architecture. - */ - @Deprecated - public boolean requestFaceAuth(@FaceAuthApiRequestReason String reason) { - mLogger.logFaceAuthRequested(reason); - updateFaceListeningState(BIOMETRIC_ACTION_START, apiRequestReasonToUiEvent(reason)); - return isFaceDetectionRunning(); - } - - /** - * In case face auth is running, cancel it. - */ - public void cancelFaceAuth() { - stopListeningForFace(FACE_AUTH_STOPPED_USER_INPUT_ON_BOUNCER); - } - - private void updateFaceListeningState(int action, @NonNull FaceAuthUiEvent faceAuthUiEvent) { - if (isFaceAuthInteractorEnabled()) return; - // If this message exists, we should not authenticate again until this message is - // consumed by the handler - if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) { - return; - } - mHandler.removeCallbacks(mRetryFaceAuthentication); - boolean shouldListenForFace = shouldListenForFace(); - if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) { - if (action == BIOMETRIC_ACTION_START) { - mLogger.v("Ignoring stopListeningForFace()"); - return; - } - stopListeningForFace(faceAuthUiEvent); - } else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING && shouldListenForFace) { - if (action == BIOMETRIC_ACTION_STOP) { - mLogger.v("Ignoring startListeningForFace()"); - return; - } - startListeningForFace(faceAuthUiEvent); - } - } - @Nullable private InstanceId getKeyguardSessionId() { return mSessionTrackerProvider.get().getSessionId(SESSION_KEYGUARD); @@ -2994,8 +2609,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @NonNull ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin, String extraReason ) { - final boolean canFaceBypass = isFaceEnrolled() && mKeyguardBypassController != null - && mKeyguardBypassController.canBypass(); + final boolean canFaceBypass = + isFaceEnabledAndEnrolled() && mKeyguardBypassController != null + && mKeyguardBypassController.canBypass(); requestActiveUnlock( requestOrigin, extraReason, canFaceBypass @@ -3022,8 +2638,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public void setAlternateBouncerShowing(boolean showing) { mAlternateBouncerShowing = showing; if (mAlternateBouncerShowing) { - updateFaceListeningState(BIOMETRIC_ACTION_START, - FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN); requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT, "alternateBouncer"); @@ -3101,17 +2715,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mSelectedUserInteractor.getSelectedUserId(), false); } - private boolean shouldListenForFaceAssistant() { - BiometricAuthenticated face = mUserFaceAuthenticated.get( - mSelectedUserInteractor.getSelectedUserId()); - return mAssistantVisible - // There can be intermediate states where mKeyguardShowing is false but - // mKeyguardOccluded is true, we don't want to run face auth in such a scenario. - && (mKeyguardShowing && mKeyguardOccluded) - && !(face != null && face.mAuthenticated) - && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false); - } - private boolean shouldTriggerActiveUnlockForAssistant() { return mAssistantVisible && mKeyguardOccluded && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false); @@ -3195,107 +2798,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * If face auth is allows to scan on this exact moment. + * + * @deprecated Use {@link KeyguardFaceAuthInteractor#canFaceAuthRun()} */ + @Deprecated public boolean shouldListenForFace() { - if (mFaceManager == null) { - // Device does not have face auth - return false; - } - - if (isFaceAuthInteractorEnabled()) { - return mFaceAuthInteractor.canFaceAuthRun(); - } - - final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED; - final boolean awakeKeyguard = isKeyguardVisible() && mDeviceInteractive - && !statusBarShadeLocked; - final int user = mSelectedUserInteractor.getSelectedUserId(); - final boolean faceAuthAllowed = isUnlockingWithBiometricAllowed(FACE); - final boolean canBypass = mKeyguardBypassController != null - && mKeyguardBypassController.canBypass(); - // There's no reason to ask the HAL for authentication when the user can dismiss the - // bouncer because the user is trusted, unless we're bypassing and need to auto-dismiss - // the lock screen even when TrustAgents are keeping the device unlocked. - final boolean userNotTrustedOrDetectionIsNeeded = !getUserHasTrust(user) || canBypass; - - // If the device supports face detection (without authentication), if bypass is enabled, - // allow face detection to happen even if stronger auth is required. When face is detected, - // we show the bouncer. However, if the user manually locked down the device themselves, - // never attempt to detect face. - final boolean supportsDetect = isFaceSupported() - && mFaceSensorProperties.get(0).supportsFaceDetection - && canBypass && !mPrimaryBouncerIsOrWillBeShowing - && !isUserInLockdown(user); - final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect; - - // If the face or fp has recently been authenticated do not attempt to authenticate again. - final boolean faceAndFpNotAuthenticated = !getUserUnlockedWithBiometric(user); - final boolean faceDisabledForUser = isFaceDisabled(user); - final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user); - final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant(); - final boolean isUdfpsFingerDown = mAuthController.isUdfpsFingerDown(); - final boolean isPostureAllowedForFaceAuth = doesPostureAllowFaceAuth(mPostureState); - // Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an - // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. - final boolean shouldListen = - (mPrimaryBouncerFullyShown - || mAuthInterruptActive - || mOccludingAppRequestingFace - || awakeKeyguard - || shouldListenForFaceAssistant - || isUdfpsFingerDown - || mAlternateBouncerShowing) - && !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded - && !mKeyguardGoingAway && biometricEnabledForUser - && faceAuthAllowedOrDetectionIsNeeded && mIsSystemUser - && (!mSecureCameraLaunched || mAlternateBouncerShowing) - && faceAndFpNotAuthenticated - && !mGoingToSleep - && isPostureAllowedForFaceAuth - && mAllowedDisplayStateWhileAwakeForFaceAuth; - - // Aggregate relevant fields for debug logging. - logListenerModelData( - new KeyguardFaceListenModel( - System.currentTimeMillis(), - user, - shouldListen, - mAllowedDisplayStateWhileAwakeForFaceAuth, - mAlternateBouncerShowing, - mAuthInterruptActive, - biometricEnabledForUser, - mPrimaryBouncerFullyShown, - faceAndFpNotAuthenticated, - faceAuthAllowed, - faceDisabledForUser, - isFaceLockedOut(), - mGoingToSleep, - awakeKeyguard, - mKeyguardGoingAway, - shouldListenForFaceAssistant, - mOccludingAppRequestingFace, - isPostureAllowedForFaceAuth, - mSecureCameraLaunched, - supportsDetect, - mSwitchingUser, - mIsSystemUser, - isUdfpsFingerDown, - userNotTrustedOrDetectionIsNeeded)); - - return shouldListen; - } - - private boolean doesPostureAllowFaceAuth(@DevicePostureInt int posture) { - return mConfigFaceAuthSupportedPosture == DEVICE_POSTURE_UNKNOWN - || (posture == mConfigFaceAuthSupportedPosture); + return getFaceAuthInteractor() != null && getFaceAuthInteractor().canFaceAuthRun(); } - /** - * If the current device posture allows face auth to run. - */ - public boolean doesCurrentPostureAllowFaceAuth() { - return doesPostureAllowFaceAuth(mPostureState); - } private void logListenerModelData(@NonNull KeyguardListenModel model) { mLogger.logKeyguardListenerModel(model); @@ -3303,8 +2813,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFingerprintListenBuffer.insert((KeyguardFingerprintListenModel) model); } else if (model instanceof KeyguardActiveUnlockModel) { mActiveUnlockTriggerBuffer.insert((KeyguardActiveUnlockModel) model); - } else if (model instanceof KeyguardFaceListenModel) { - mFaceListenBuffer.insert((KeyguardFaceListenModel) model); } } @@ -3355,85 +2863,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void startListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) { - final int userId = mSelectedUserInteractor.getSelectedUserId(); - final boolean unlockPossible = isUnlockWithFacePossible(userId); - if (mFaceCancelSignal != null) { - mLogger.logUnexpectedFaceCancellationSignalState(mFaceRunningState, unlockPossible); - } - - if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) { - setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING); - return; - } else if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) { - // Waiting for ERROR_CANCELED before requesting auth again - return; - } - mLogger.logStartedListeningForFace(mFaceRunningState, faceAuthUiEvent); - mUiEventLogger.logWithInstanceIdAndPosition( - faceAuthUiEvent, - 0, - null, - getKeyguardSessionId(), - faceAuthUiEvent.getExtraInfo() - ); - mLogger.logFaceUnlockPossible(unlockPossible); - if (unlockPossible) { - mFaceCancelSignal = new CancellationSignal(); - - final FaceAuthenticateOptions faceAuthenticateOptions = - new SysUiFaceAuthenticateOptions( - userId, - faceAuthUiEvent, - faceAuthUiEvent == FACE_AUTH_UPDATED_STARTED_WAKING_UP - ? faceAuthUiEvent.getExtraInfo() - : WAKE_REASON_UNKNOWN - ).toFaceAuthenticateOptions(); - // This would need to be updated for multi-sensor devices - final boolean supportsFaceDetection = isFaceSupported() - && mFaceSensorProperties.get(0).supportsFaceDetection; - if (!isUnlockingWithBiometricAllowed(FACE)) { - final boolean udfpsFingerprintAuthRunning = isUdfpsSupported() - && isFingerprintDetectionRunning(); - if (supportsFaceDetection && !udfpsFingerprintAuthRunning) { - // Run face detection. (If a face is detected, show the bouncer.) - mLogger.v("startListeningForFace - detect"); - mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, - faceAuthenticateOptions); - } else { - // Don't run face detection. Instead, inform the user - // face auth is unavailable and how to proceed. - // (ie: "Use fingerprint instead" or "Swipe up to open") - mLogger.v("Ignoring \"startListeningForFace - detect\". " - + "Informing user face isn't available."); - mFaceAuthenticationCallback.onAuthenticationHelp( - BIOMETRIC_HELP_FACE_NOT_AVAILABLE, - mContext.getResources().getString( - R.string.keyguard_face_unlock_unavailable) - ); - return; - } - } else { - mLogger.v("startListeningForFace - authenticate"); - final boolean isBypassEnabled = mKeyguardBypassController != null - && mKeyguardBypassController.isBypassEnabled(); - mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal, - mFaceAuthenticationCallback, null /* handler */, - faceAuthenticateOptions); - } - setFaceRunningState(BIOMETRIC_STATE_RUNNING); - } - } - public boolean isFingerprintLockedOut() { return mFingerprintLockedOut || mFingerprintLockedOutPermanent; } + /** + * @deprecated Use {@link KeyguardFaceAuthInteractor#isLockedOut()} + */ + @Deprecated public boolean isFaceLockedOut() { - if (isFaceAuthInteractorEnabled()) { - return getFaceAuthInteractor().isLockedOut(); - } - return mFaceLockedOutPermanent; + return getFaceAuthInteractor() != null && getFaceAuthInteractor().isLockedOut(); } /** @@ -3444,7 +2883,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * @return {@code true} if possible. */ public boolean isUnlockingWithBiometricsPossible(int userId) { - return isUnlockWithFacePossible(userId) || isUnlockWithFingerprintPossible(userId); + return isUnlockWithFacePossible() || isUnlockWithFingerprintPossible(userId); } /** @@ -3455,8 +2894,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * @return {@code true} if possible. */ public boolean isUnlockingWithNonStrongBiometricsPossible(int userId) { - return (!isFaceClass3() && isUnlockWithFacePossible(userId)) - || (isFingerprintClass3() && isUnlockWithFingerprintPossible(userId)); + if (getFaceAuthInteractor() != null && !getFaceAuthInteractor().isFaceAuthStrong()) { + if (isUnlockWithFacePossible()) { + return true; + } + } + return isFingerprintClass3() && isUnlockWithFingerprintPossible(userId); } @SuppressLint("MissingPermission") @@ -3466,16 +2909,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** - * @deprecated This is being migrated to use modern architecture. + * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()} */ @VisibleForTesting @Deprecated - public boolean isUnlockWithFacePossible(int userId) { - if (isFaceAuthInteractorEnabled()) { - return getFaceAuthInteractor() != null + public boolean isUnlockWithFacePossible() { + return getFaceAuthInteractor() != null && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled(); - } - return isFaceSupported() && isFaceEnrolled(userId) && !isFaceDisabled(userId); } private void notifyAboutEnrollmentChange(@BiometricAuthenticator.Modality int modality) { @@ -3513,25 +2953,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void stopListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) { - if (isFaceAuthInteractorEnabled()) return; - mLogger.v("stopListeningForFace()"); - mLogger.logStoppedListeningForFace(mFaceRunningState, faceAuthUiEvent.getReason()); - mUiEventLogger.log(faceAuthUiEvent, getKeyguardSessionId()); - if (mFaceRunningState == BIOMETRIC_STATE_RUNNING) { - if (mFaceCancelSignal != null) { - mFaceCancelSignal.cancel(); - mFaceCancelSignal = null; - mHandler.removeCallbacks(mFaceCancelNotReceived); - mHandler.postDelayed(mFaceCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT); - } - setFaceRunningState(BIOMETRIC_STATE_CANCELLING); - } - if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) { - setFaceRunningState(BIOMETRIC_STATE_CANCELLING); - } - } - private boolean isDeviceProvisionedInSettingsDb() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; @@ -3617,13 +3038,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - // Immediately stop previous biometric listening states. - // Resetting lockout states updates the biometric listening states. - if (isFaceSupported()) { - stopListeningForFace(FACE_AUTH_UPDATED_USER_SWITCHING); - handleFaceLockoutReset(mFaceManager.getLockoutModeForUser( - mFaceSensorProperties.get(0).sensorId, userId)); - } if (isFingerprintSupported()) { stopListeningForFingerprint(); handleFingerprintLockoutReset(mFpm.getLockoutModeForUser( @@ -3866,8 +3280,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting protected void handleKeyguardReset() { mLogger.d("handleKeyguardReset"); - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_KEYGUARD_RESET); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition(); } @@ -3935,8 +3348,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onKeyguardBouncerFullyShowingChanged(mPrimaryBouncerFullyShown); } } - updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN); } } @@ -4071,8 +3482,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } mSwitchingUser = switching; // Since this comes in on a binder thread, we need to post it first - mHandler.post(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FACE_AUTH_UPDATED_USER_SWITCHING)); + mHandler.post(() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE)); } private void sendUpdates(KeyguardUpdateMonitorCallback callback) { @@ -4161,7 +3571,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void clearBiometricRecognized(int unlockedUser) { Assert.isMainThread(); mUserFingerprintAuthenticated.clear(); - mUserFaceAuthenticated.clear(); mTrustManager.clearAllBiometricRecognized(FINGERPRINT, unlockedUser); mTrustManager.clearAllBiometricRecognized(FACE, unlockedUser); mLogger.d("clearBiometricRecognized"); @@ -4394,12 +3803,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return isFingerprintSupported() && isClass3Biometric(mFingerprintSensorProperties.get(0)); } - @VisibleForTesting - protected boolean isFaceClass3() { - // This assumes that there is at most one face sensor property - return isFaceSupported() && isClass3Biometric(mFaceSensorProperties.get(0)); - } - private boolean isClass3Biometric(SensorPropertiesInternal sensorProperties) { return sensorProperties.sensorStrength == SensorProperties.STRENGTH_STRONG; } @@ -4411,8 +3814,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mStatusBarStateController.removeCallback(mStatusBarStateControllerListener); mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener); mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener); - if (isFaceAuthInteractorEnabled()) { - mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener); + if (getFaceAuthInteractor() != null) { + getFaceAuthInteractor().unregisterListener(mFaceAuthenticationListener); } if (mDeviceProvisionedObserver != null) { @@ -4432,7 +3835,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLockPatternUtils.unregisterStrongAuthTracker(mStrongAuthTracker); mTrustManager.unregisterTrustListener(this); - mDisplayTracker.removeCallback(mDisplayCallback); mHandler.removeCallbacksAndMessages(null); } @@ -4446,7 +3848,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mSelectedUserInteractor.getSelectedUserId())); pw.println(" getUserUnlockedWithBiometric()=" + getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId())); - pw.println(" isFaceAuthInteractorEnabled: " + isFaceAuthInteractorEnabled()); pw.println(" SIM States:"); for (SimData data : mSimDatas.values()) { pw.println(" " + data.toString()); @@ -4519,50 +3920,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFingerprintListenBuffer.toList() ).printTableData(pw); } - if (isFaceSupported()) { - final int userId = mSelectedUserInteractor.getSelectedUserId(true); - final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId); - BiometricAuthenticated face = mUserFaceAuthenticated.get(userId); - pw.println(" Face authentication state (user=" + userId + ")"); - pw.println(" isFaceClass3=" + isFaceClass3()); - pw.println(" allowed=" - + (face != null && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric))); - pw.println(" auth'd=" - + (face != null && face.mAuthenticated)); - pw.println(" authSinceBoot=" - + getStrongAuthTracker().hasUserAuthenticatedSinceBoot()); - pw.println(" disabled(DPM)=" + isFaceDisabled(userId)); - pw.println(" possible=" + isUnlockWithFacePossible(userId)); - pw.println(" listening: actual=" + mFaceRunningState - + " expected=(" + (shouldListenForFace() ? 1 : 0)); - pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); - pw.println(" isNonStrongBiometricAllowedAfterIdleTimeout=" - + mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(userId)); - pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); - pw.println(" mFaceLockedOutPermanent=" + mFaceLockedOutPermanent); - pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId)); - pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched); - pw.println(" mPrimaryBouncerFullyShown=" + mPrimaryBouncerFullyShown); - pw.println(" mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition); - new DumpsysTableLogger( - "KeyguardFaceListen", - KeyguardFaceListenModel.TABLE_HEADERS, - mFaceListenBuffer.toList() - ).printTableData(pw); - } else if (mFaceManager != null && mFaceSensorProperties.isEmpty()) { - final int userId = mSelectedUserInteractor.getSelectedUserId(true); - pw.println(" Face state (user=" + userId + ")"); - pw.println(" mFaceSensorProperties.isEmpty=" - + mFaceSensorProperties.isEmpty()); - pw.println(" mFaceManager.isHardwareDetected=" - + mFaceManager.isHardwareDetected()); - - new DumpsysTableLogger( - "KeyguardFaceListen", - KeyguardFingerprintListenModel.TABLE_HEADERS, - mFingerprintListenBuffer.toList() - ).printTableData(pw); - } + final int userId = mSelectedUserInteractor.getSelectedUserId(true); + final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId); + pw.println(" authSinceBoot=" + + getStrongAuthTracker().hasUserAuthenticatedSinceBoot()); + pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); pw.println("ActiveUnlockRunning=" + mTrustManager.isActiveUnlockRunning(mSelectedUserInteractor.getSelectedUserId())); new DumpsysTableLogger( @@ -4577,13 +3939,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * Cancels all operations in the scheduler if it is hung for 10 seconds. */ public void startBiometricWatchdog() { - final boolean isFaceAuthInteractorEnabled = isFaceAuthInteractorEnabled(); mBackgroundExecutor.execute(() -> { Trace.beginSection("#startBiometricWatchdog"); - if (mFaceManager != null && !isFaceAuthInteractorEnabled) { - mLogger.scheduleWatchdog("face"); - mFaceManager.scheduleWatchdog(); - } if (mFpm != null) { mLogger.scheduleWatchdog("fingerprint"); mFpm.scheduleWatchdog(); diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index 5bf8d635f8ee..055ca565a933 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -20,14 +20,12 @@ import android.content.Intent import android.hardware.biometrics.BiometricConstants.LockoutMode import android.hardware.biometrics.BiometricSourceType import android.os.PowerManager -import android.os.PowerManager.WakeReason import android.telephony.ServiceState import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.telephony.TelephonyManager import com.android.keyguard.ActiveUnlockConfig -import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.KeyguardListenModel import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.TrustGrantFlags @@ -102,14 +100,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { logBuffer.log(TAG, ERROR, {}, { logMsg }, exception = ex) } - fun logFaceAuthDisabledForUser(userId: Int) { - logBuffer.log( - TAG, - DEBUG, - { int1 = userId }, - { "Face authentication disabled by DPM for userId: $int1" } - ) - } fun logFaceAuthError(msgId: Int, originalErrMsg: String) { logBuffer.log( TAG, @@ -131,31 +121,10 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } - fun logFaceAuthRequested(reason: String?) { - logBuffer.log(TAG, DEBUG, { str1 = reason }, { "requestFaceAuth() reason=$str1" }) - } - fun logFaceAuthSuccess(userId: Int) { logBuffer.log(TAG, DEBUG, { int1 = userId }, { "Face auth succeeded for user $int1" }) } - fun logFaceLockoutReset(@LockoutMode mode: Int) { - logBuffer.log(TAG, DEBUG, { int1 = mode }, { "handleFaceLockoutReset: $int1" }) - } - - fun logFaceRunningState(faceRunningState: Int) { - logBuffer.log(TAG, DEBUG, { int1 = faceRunningState }, { "faceRunningState: $int1" }) - } - - fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) { - logBuffer.log( - TAG, - DEBUG, - { bool1 = isFaceUnlockPossible }, - { "isUnlockWithFacePossible: $bool1" } - ) - } - fun logFingerprintAuthForWrongUser(authUserId: Int) { logBuffer.log( FP_LOG_TAG, @@ -301,15 +270,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** register callback for $str1" }) } - fun logRetryingAfterFaceHwUnavailable(retryCount: Int) { - logBuffer.log( - TAG, - WARNING, - { int1 = retryCount }, - { "Retrying face after HW unavailable, attempt $int1" } - ) - } - fun logRetryAfterFpErrorWithDelay(msgId: Int, errString: String?, delay: Int) { logBuffer.log( TAG, @@ -419,43 +379,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { logBuffer.log(TAG, VERBOSE, { int1 = subId }, { "reportSimUnlocked(subId=$int1)" }) } - fun logStartedListeningForFace(faceRunningState: Int, faceAuthUiEvent: FaceAuthUiEvent) { - logBuffer.log( - TAG, - VERBOSE, - { - int1 = faceRunningState - str1 = faceAuthUiEvent.reason - str2 = faceAuthUiEvent.extraInfoToString() - }, - { "startListeningForFace(): $int1, reason: $str1 $str2" } - ) - } - - fun logStartedListeningForFaceFromWakeUp(faceRunningState: Int, @WakeReason pmWakeReason: Int) { - logBuffer.log( - TAG, - VERBOSE, - { - int1 = faceRunningState - str1 = PowerManager.wakeReasonToString(pmWakeReason) - }, - { "startListeningForFace(): $int1, reason: wakeUp-$str1" } - ) - } - - fun logStoppedListeningForFace(faceRunningState: Int, faceAuthReason: String) { - logBuffer.log( - TAG, - VERBOSE, - { - int1 = faceRunningState - str1 = faceAuthReason - }, - { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" } - ) - } - fun logSubInfo(subInfo: SubscriptionInfo?) { logBuffer.log(TAG, DEBUG, { str1 = "$subInfo" }, { "SubInfo:$str1" }) } @@ -476,22 +399,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerUp, sensorId: $int1" }) } - fun logUnexpectedFaceCancellationSignalState(faceRunningState: Int, unlockPossible: Boolean) { - logBuffer.log( - TAG, - ERROR, - { - int1 = faceRunningState - bool1 = unlockPossible - }, - { - "Cancellation signal is not null, high chance of bug in " + - "face auth lifecycle management. " + - "Face state: $int1, unlockPossible: $bool1" - } - ) - } - fun logUnexpectedFpCancellationSignalState( fingerprintRunningState: Int, unlockPossible: Boolean @@ -588,15 +495,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } - fun logSkipUpdateFaceListeningOnWakeup(@WakeReason pmWakeReason: Int) { - logBuffer.log( - TAG, - VERBOSE, - { str1 = PowerManager.wakeReasonToString(pmWakeReason) }, - { "Skip updating face listening state on wakeup from $str1" } - ) - } - fun logTaskStackChangedForAssistant(assistantVisible: Boolean) { logBuffer.log( TAG, @@ -648,18 +546,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } - fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) { - logBuffer.log( - TAG, - DEBUG, - { - bool1 = oldValue - bool2 = newValue - }, - { "Face enrolled state changed: old: $bool1, new: $bool2" } - ) - } - fun logTrustUsuallyManagedUpdated( userId: Int, oldValue: Boolean, @@ -745,18 +631,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } - fun logFingerprintHelp(helpMsgId: Int, helpString: CharSequence) { - logBuffer.log( - FP_LOG_TAG, - DEBUG, - { - int1 = helpMsgId - str1 = "$helpString" - }, - { "fingerprint help message: $int1, $str1" } - ) - } - fun logFingerprintAcquired(acquireInfo: Int) { logBuffer.log( FP_LOG_TAG, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 57e252dd9929..8fe42b536b1e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -100,7 +100,6 @@ import javax.inject.Inject; import javax.inject.Provider; import kotlin.Unit; - import kotlinx.coroutines.CoroutineScope; /** @@ -1099,6 +1098,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, // TODO(b/141025588): Create separate methods for handling hard and soft errors. final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT + || error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL || isCameraPrivacyEnabled); if (mCurrentDialog != null) { if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt index bbdcadbb19c6..cb750493ba26 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt @@ -20,12 +20,10 @@ import android.content.res.Resources import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityNodeInfo -import com.android.keyguard.FaceAuthApiRequestReason -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.res.R import javax.inject.Inject /** @@ -37,12 +35,11 @@ class FaceAuthAccessibilityDelegate @Inject constructor( @Main private val resources: Resources, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val faceAuthInteractor: KeyguardFaceAuthInteractor, ) : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) { super.onInitializeAccessibilityNodeInfo(host, info) - if (keyguardUpdateMonitor.shouldListenForFace()) { + if (faceAuthInteractor.canFaceAuthRun()) { val clickActionToRetryFace = AccessibilityNodeInfo.AccessibilityAction( AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, @@ -54,7 +51,6 @@ constructor( override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean { return if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id) { - keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.ACCESSIBILITY_ACTION) faceAuthInteractor.onAccessibilityAction() true } else super.performAccessibilityAction(host, action, args) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index b064391f74b5..85a119c389c1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -52,6 +52,7 @@ import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -63,7 +64,6 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.internal.util.LatencyTracker; -import com.android.keyguard.FaceAuthApiRequestReason; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -78,9 +78,9 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -150,7 +150,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; @NonNull private final Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels; @NonNull private final VibratorHelper mVibrator; - @NonNull private final FeatureFlags mFeatureFlags; @NonNull private final FalsingManager mFalsingManager; @NonNull private final PowerManager mPowerManager; @NonNull private final AccessibilityManager mAccessibilityManager; @@ -281,7 +280,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { fromUdfpsView ), mActivityLaunchAnimator, - mFeatureFlags, mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsKeyguardAccessibilityDelegate, @@ -318,10 +316,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { return; } mAcquiredReceived = true; - final UdfpsView view = mOverlay.getOverlayView(); - if (view != null && isOptical()) { - unconfigureDisplay(view); - } + final View view = mOverlay.getTouchOverlay(); + unconfigureDisplay(view); tryAodSendFingerUp(); }); } @@ -339,7 +335,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mOverlay == null || mOverlay.isHiding()) { return; } - mOverlay.getOverlayView().setDebugMessage(message); + if (!DeviceEntryUdfpsRefactor.isEnabled()) { + ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message); + } }); } @@ -506,6 +504,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f && !mAlternateBouncerInteractor.isVisibleState()) || mPrimaryBouncerInteractor.isInTransit()) { + Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor"); return false; } if (event.getAction() == MotionEvent.ACTION_DOWN @@ -563,7 +562,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } mAttemptedToDismissKeyguard = false; onFingerUp(requestId, - mOverlay.getOverlayView(), + mOverlay.getTouchOverlay(), data.getPointerId(), data.getX(), data.getY(), @@ -597,7 +596,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (shouldPilfer && !mPointerPilfered && getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) { mInputManager.pilferPointers( - mOverlay.getOverlayView().getViewRootImpl().getInputToken()); + mOverlay.getTouchOverlay().getViewRootImpl().getInputToken()); mPointerPilfered = true; } @@ -605,9 +604,15 @@ public class UdfpsController implements DozeReceiver, Dumpable { } private boolean shouldTryToDismissKeyguard() { - return mOverlay != null - && mOverlay.getAnimationViewController() - instanceof UdfpsKeyguardViewControllerAdapter + boolean onKeyguard = false; + if (DeviceEntryUdfpsRefactor.isEnabled()) { + onKeyguard = mKeyguardStateController.isShowing(); + } else { + onKeyguard = mOverlay != null + && mOverlay.getAnimationViewController() + instanceof UdfpsKeyguardViewControllerAdapter; + } + return onKeyguard && mKeyguardStateController.canDismissLockScreen() && !mAttemptedToDismissKeyguard; } @@ -623,7 +628,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, @NonNull DumpManager dumpManager, @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, - @NonNull FeatureFlags featureFlags, @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, @NonNull AccessibilityManager accessibilityManager, @@ -670,7 +674,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mDumpManager = dumpManager; mDialogManager = dialogManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mFeatureFlags = featureFlags; mFalsingManager = falsingManager; mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; @@ -737,9 +740,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { @VisibleForTesting public void playStartHaptic() { if (mAccessibilityManager.isTouchExplorationEnabled()) { - if (mOverlay != null && mOverlay.getOverlayView() != null) { + if (mOverlay != null && mOverlay.getTouchOverlay() != null) { mVibrator.performHapticFeedback( - mOverlay.getOverlayView(), + mOverlay.getTouchOverlay(), HapticFeedbackConstants.CONTEXT_CLICK ); } else { @@ -751,10 +754,11 @@ public class UdfpsController implements DozeReceiver, Dumpable { @Override public void dozeTimeTick() { - if (mOverlay != null) { - final UdfpsView view = mOverlay.getOverlayView(); + if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) { + DeviceEntryUdfpsRefactor.assertInLegacyMode(); + final View view = mOverlay.getTouchOverlay(); if (view != null) { - view.dozeTimeTick(); + ((UdfpsView) view).dozeTimeTick(); } } } @@ -797,7 +801,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mOverlay != null) { // Reset the controller back to its starting state. - final UdfpsView oldView = mOverlay.getOverlayView(); + final View oldView = mOverlay.getTouchOverlay(); if (oldView != null) { onFingerUp(mOverlay.getRequestId(), oldView); } @@ -813,9 +817,21 @@ public class UdfpsController implements DozeReceiver, Dumpable { } - private void unconfigureDisplay(@NonNull UdfpsView view) { - if (view.isDisplayConfigured()) { - view.unconfigureDisplay(); + private void unconfigureDisplay(View view) { + if (!isOptical()) { + return; + } + if (DeviceEntryUdfpsRefactor.isEnabled()) { + if (mUdfpsDisplayMode != null) { + mUdfpsDisplayMode.disable(null); // beverlt + } + } else { + if (view != null) { + UdfpsView udfpsView = (UdfpsView) view; + if (udfpsView.isDisplayConfigured()) { + udfpsView.unconfigureDisplay(); + } + } } } @@ -837,10 +853,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { } mKeyguardViewManager.showPrimaryBouncer(true); - // play the same haptic as the LockIconViewController longpress - if (mOverlay != null && mOverlay.getOverlayView() != null) { + // play the same haptic as the DeviceEntryIcon longpress + if (mOverlay != null && mOverlay.getTouchOverlay() != null) { mVibrator.performHapticFeedback( - mOverlay.getOverlayView(), + mOverlay.getTouchOverlay(), UdfpsController.LONG_PRESS ); } else { @@ -909,8 +925,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { return; } cancelAodSendFingerUpAction(); - if (mOverlay != null && mOverlay.getOverlayView() != null) { - onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView()); + if (mOverlay != null && mOverlay.getTouchOverlay() != null) { + onFingerUp(mOverlay.getRequestId(), mOverlay.getTouchOverlay()); } } @@ -996,20 +1012,22 @@ public class UdfpsController implements DozeReceiver, Dumpable { playStartHaptic(); mKeyguardFaceAuthInteractor.onUdfpsSensorTouched(); - if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) { - mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); - } } mOnFingerDown = true; mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y, minor, major, orientation, time, gestureStart, isAod); Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0); - final UdfpsView view = mOverlay.getOverlayView(); + + final View view = mOverlay.getTouchOverlay(); if (view != null && isOptical()) { if (mIgnoreRefreshRate) { dispatchOnUiReady(requestId); } else { - view.configureDisplay(() -> dispatchOnUiReady(requestId)); + if (DeviceEntryUdfpsRefactor.isEnabled()) { + mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId)); + } else { + ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId)); + } } } @@ -1018,7 +1036,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } } - private void onFingerUp(long requestId, @NonNull UdfpsView view) { + private void onFingerUp(long requestId, @NonNull View view) { onFingerUp( requestId, view, @@ -1035,7 +1053,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { private void onFingerUp( long requestId, - @NonNull UdfpsView view, + View view, int pointerId, float x, float y, @@ -1056,9 +1074,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } } mOnFingerDown = false; - if (isOptical()) { - unconfigureDisplay(view); - } + unconfigureDisplay(view); cancelAodSendFingerUpAction(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index a5bd89a15e5a..2d54f7ac8e7d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -46,11 +46,11 @@ import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams +import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -96,7 +96,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val controllerCallback: IUdfpsOverlayControllerCallback, private val onTouch: (View, MotionEvent, Boolean) -> Boolean, private val activityLaunchAnimator: ActivityLaunchAnimator, - private val featureFlags: FeatureFlags, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val isDebuggable: Boolean = Build.IS_DEBUGGABLE, @@ -104,9 +103,22 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val transitionInteractor: KeyguardTransitionInteractor, private val selectedUserInteractor: SelectedUserInteractor, ) { - /** The view, when [isShowing], or null. */ - var overlayView: UdfpsView? = null + private var overlayViewLegacy: UdfpsView? = null private set + private var overlayTouchView: UdfpsTouchOverlay? = null + + /** + * Get the current UDFPS overlay touch view which is a different View depending on whether + * the DeviceEntryUdfpsRefactor flag is enabled or not. + * @return The view, when [isShowing], else null + */ + fun getTouchOverlay(): View? { + return if (DeviceEntryUdfpsRefactor.isEnabled) { + overlayTouchView + } else { + overlayViewLegacy + } + } private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() private var sensorBounds: Rect = Rect() @@ -132,15 +144,15 @@ class UdfpsControllerOverlay @JvmOverloads constructor( /** If the overlay is currently showing. */ val isShowing: Boolean - get() = overlayView != null + get() = getTouchOverlay() != null /** Opposite of [isShowing]. */ val isHiding: Boolean - get() = overlayView == null + get() = getTouchOverlay() == null /** The animation controller if the overlay [isShowing]. */ val animationViewController: UdfpsAnimationViewController<*>? - get() = overlayView?.animationViewController + get() = overlayViewLegacy?.animationViewController private var touchExplorationEnabled = false @@ -158,28 +170,48 @@ class UdfpsControllerOverlay @JvmOverloads constructor( /** Show the overlay or return false and do nothing if it is already showing. */ @SuppressLint("ClickableViewAccessibility") fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean { - if (overlayView == null) { + if (getTouchOverlay() == null) { overlayParams = params sensorBounds = Rect(params.sensorBounds) try { - overlayView = (inflater.inflate( - R.layout.udfps_view, null, false - ) as UdfpsView).apply { - overlayParams = params - setUdfpsDisplayModeProvider(udfpsDisplayModeProvider) - val animation = inflateUdfpsAnimation(this, controller) - if (animation != null) { - animation.init() - animationViewController = animation - } - // This view overlaps the sensor area - // prevent it from being selectable during a11y - if (requestReason.isImportantForAccessibility()) { - importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO + if (DeviceEntryUdfpsRefactor.isEnabled) { + overlayTouchView = (inflater.inflate( + R.layout.udfps_touch_overlay, null, false + ) as UdfpsTouchOverlay).apply { + // This view overlaps the sensor area + // prevent it from being selectable during a11y + if (requestReason.isImportantForAccessibility()) { + importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO + } + windowManager.addView(this, coreLayoutParams.updateDimensions(null)) } + // TODO (b/305234447): Bind view model to UdfpsTouchOverlay here to control + // the visibility (sometimes, even if UDFPS is running, the UDFPS UI can be + // obscured and we don't want to accept touches. ie: for enrollment don't accept + // touches when the shade is expanded and for keyguard: don't accept touches + // depending on the keyguard & shade state + } else { + overlayViewLegacy = (inflater.inflate( + R.layout.udfps_view, null, false + ) as UdfpsView).apply { + overlayParams = params + setUdfpsDisplayModeProvider(udfpsDisplayModeProvider) + val animation = inflateUdfpsAnimation(this, controller) + if (animation != null) { + animation.init() + animationViewController = animation + } + // This view overlaps the sensor area + // prevent it from being selectable during a11y + if (requestReason.isImportantForAccessibility()) { + importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO + } - windowManager.addView(this, coreLayoutParams.updateDimensions(animation)) - sensorRect = sensorBounds + windowManager.addView(this, coreLayoutParams.updateDimensions(animation)) + sensorRect = sensorBounds + } + } + getTouchOverlay()?.apply { touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled overlayTouchListener = TouchExplorationStateChangeListener { if (accessibilityManager.isTouchExplorationEnabled) { @@ -193,7 +225,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( } } accessibilityManager.addTouchExplorationStateChangeListener( - overlayTouchListener!! + overlayTouchListener!! ) overlayTouchListener?.onTouchExplorationStateChanged(true) } @@ -211,6 +243,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor( view: UdfpsView, controller: UdfpsController ): UdfpsAnimationViewController<*>? { + DeviceEntryUdfpsRefactor.assertInLegacyMode() + val isEnrollment = when (requestReason) { REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true else -> false @@ -237,39 +271,27 @@ class UdfpsControllerOverlay @JvmOverloads constructor( ) } REASON_AUTH_KEYGUARD -> { - if (DeviceEntryUdfpsRefactor.isEnabled) { - // note: empty controller, currently shows no visual affordance - // instead SysUI will show the fingerprint icon in its DeviceEntryIconView - UdfpsBpViewController( - view.addUdfpsView(R.layout.udfps_bp_view), - statusBarStateController, - primaryBouncerInteractor, - dialogManager, - dumpManager - ) - } else { - UdfpsKeyguardViewControllerLegacy( - view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) { - updateSensorLocation(sensorBounds) - }, - statusBarStateController, - statusBarKeyguardViewManager, - keyguardUpdateMonitor, - dumpManager, - transitionController, - configurationController, - keyguardStateController, - unlockedScreenOffAnimationController, - dialogManager, - controller, - activityLaunchAnimator, - primaryBouncerInteractor, - alternateBouncerInteractor, - udfpsKeyguardAccessibilityDelegate, - selectedUserInteractor, - transitionInteractor, - ) - } + UdfpsKeyguardViewControllerLegacy( + view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) { + updateSensorLocation(sensorBounds) + }, + statusBarStateController, + statusBarKeyguardViewManager, + keyguardUpdateMonitor, + dumpManager, + transitionController, + configurationController, + keyguardStateController, + unlockedScreenOffAnimationController, + dialogManager, + controller, + activityLaunchAnimator, + primaryBouncerInteractor, + alternateBouncerInteractor, + udfpsKeyguardAccessibilityDelegate, + selectedUserInteractor, + transitionInteractor, + ) } REASON_AUTH_BP -> { // note: empty controller, currently shows no visual affordance @@ -302,19 +324,26 @@ class UdfpsControllerOverlay @JvmOverloads constructor( fun hide(): Boolean { val wasShowing = isShowing - overlayView?.apply { + overlayViewLegacy?.apply { if (isDisplayConfigured) { unconfigureDisplay() } + animationViewController = null + } + if (DeviceEntryUdfpsRefactor.isEnabled) { + udfpsDisplayModeProvider.disable(null) + } + getTouchOverlay()?.apply { windowManager.removeView(this) setOnTouchListener(null) setOnHoverListener(null) - animationViewController = null overlayTouchListener?.let { accessibilityManager.removeTouchExplorationStateChangeListener(it) } } - overlayView = null + + overlayViewLegacy = null + overlayTouchView = null overlayTouchListener = null return wasShowing @@ -392,7 +421,14 @@ class UdfpsControllerOverlay @JvmOverloads constructor( } private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean { - if (animation !is UdfpsKeyguardViewControllerAdapter) { + val keyguardNotShowing = + if (DeviceEntryUdfpsRefactor.isEnabled) { + !keyguardStateController.isShowing + } else { + animation !is UdfpsKeyguardViewControllerAdapter + } + + if (keyguardNotShowing) { // always rotate view if we're not on the keyguard return true } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 35c3ded9e984..6954eb66cfa6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -50,7 +50,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge import kotlinx.coroutines.launch /** Class that coordinates non-HBM animations during keyguard authentication. */ @@ -215,12 +214,12 @@ open class UdfpsKeyguardViewControllerLegacy( suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job { return scope.launch { transitionInteractor.dozeAmountTransition.collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } + view.onDozeAmountChanged( + transitionStep.value, + transitionStep.value, + UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, + ) + } } } @@ -286,7 +285,6 @@ open class UdfpsKeyguardViewControllerLegacy( keyguardStateController.removeCallback(keyguardStateControllerCallback) statusBarStateController.removeCallback(stateListener) keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI) - keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false) configurationController.removeCallback(configurationListener) if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) { lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null @@ -334,14 +332,9 @@ open class UdfpsKeyguardViewControllerLegacy( if (udfpsAffordanceWasNotShowing) { view.animateInUdfpsBouncer(null) } - if (keyguardStateController.isOccluded) { - keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true) - } view.announceForAccessibility( view.context.getString(R.string.accessibility_fingerprint_bouncer) ) - } else { - keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false) } updateAlpha() updatePauseAuth() diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt new file mode 100644 index 000000000000..2484c339a1d4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 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.biometrics.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout + +/** + * A translucent (not visible to the user) view that receives touches to send to FingerprintManager + * for fingerprint authentication. + */ +class UdfpsTouchOverlay(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt index 56dfa5ed337c..aa7758f9380f 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt @@ -39,6 +39,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.TrustRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.shared.system.SysUiStatsLog @@ -75,6 +76,7 @@ constructor( private val trustRepository: TrustRepository, @Application private val applicationScope: CoroutineScope, private val selectedUserInteractor: SelectedUserInteractor, + private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor, ) { private val passiveAuthBouncerDelay = context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong() @@ -414,15 +416,12 @@ constructor( /** Whether we want to wait to show the bouncer in case passive auth succeeds. */ private fun usePrimaryBouncerPassiveAuthDelay(): Boolean { - val canRunFaceAuth = - keyguardStateController.isFaceEnrolled && - keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) && - keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth() val canRunActiveUnlock = currentUserActiveUnlockRunning && keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState() - return !needsFullscreenBouncer() && (canRunFaceAuth || canRunActiveUnlock) + return !needsFullscreenBouncer() && + (keyguardFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index fd7f641c2e61..e630fd4e3c54 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -27,6 +27,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.smartspace.data.repository.SmartspaceRepository import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -46,6 +47,7 @@ constructor( private val widgetRepository: CommunalWidgetRepository, mediaRepository: CommunalMediaRepository, smartspaceRepository: SmartspaceRepository, + keyguardInteractor: KeyguardInteractor, private val appWidgetHost: AppWidgetHost, private val editWidgetsActivityStarter: EditWidgetsActivityStarter ) { @@ -67,6 +69,8 @@ constructor( val isCommunalShowing: Flow<Boolean> = communalRepository.desiredScene.map { it == CommunalSceneKey.Communal } + val isKeyguardVisible: Flow<Boolean> = keyguardInteractor.isKeyguardVisible + /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */ fun onSceneChanged(newScene: CommunalSceneKey) { communalRepository.setDesiredScene(newScene) diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index b4ab5fbfcc1a..4d8e893cb747 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -28,6 +28,8 @@ abstract class BaseCommunalViewModel( private val communalInteractor: CommunalInteractor, val mediaHost: MediaHost, ) { + val isKeyguardVisible: Flow<Boolean> = communalInteractor.isKeyguardVisible + val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene fun onSceneChanged(scene: CommunalSceneKey) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index eaf2d5741e23..11bde6bd7af0 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -39,7 +39,6 @@ constructor( tutorialInteractor: CommunalTutorialInteractor, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, ) : BaseCommunalViewModel(communalInteractor, mediaHost) { - @OptIn(ExperimentalCoroutinesApi::class) override val communalContent: Flow<List<CommunalContentModel>> = tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode -> diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt index 31a5d37a4450..4bfc9484f733 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt @@ -97,7 +97,7 @@ class FaceScanningProviderFactory @Inject constructor( } fun canShowFaceScanningAnim(): Boolean { - return hasProviders && keyguardUpdateMonitor.isFaceEnrolled + return hasProviders && keyguardUpdateMonitor.isFaceEnabledAndEnrolled } fun shouldShowFaceScanningAnim(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 83c16ae9ea78..6a0e88246027 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -19,6 +19,7 @@ package com.android.systemui.flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import javax.inject.Inject /** A class in which engineers can define flag dependencies */ @@ -26,6 +27,7 @@ import javax.inject.Inject class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, handler: Handler) : FlagDependenciesBase(featureFlags, handler) { override fun defineDependencies() { + NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 7d541070f146..b357b563f791 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -152,10 +152,6 @@ object Flags { @JvmField val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true) - /** Flag to control the migration of face auth to modern architecture. */ - // TODO(b/262838215): Tracking bug - @JvmField val FACE_AUTH_REFACTOR = releasedFlag("face_auth_refactor") - /** Flag to control the revamp of keyguard biometrics progress animation */ // TODO(b/244313043): Tracking bug @JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp") @@ -464,12 +460,6 @@ object Flags { val WALLPAPER_MULTI_CROP = sysPropBooleanFlag("persist.wm.debug.wallpaper_multi_crop", default = false) - // TODO(b/290220798): Tracking Bug - @Keep - @JvmField - val ENABLE_PIP2_IMPLEMENTATION = - sysPropBooleanFlag("persist.wm.debug.enable_pip2_implementation", default = false) - // 1200 - predictive back @Keep @JvmField @@ -713,7 +703,7 @@ object Flags { /** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */ @JvmField - val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog") + val BLUETOOTH_QS_TILE_DIALOG = releasedFlag("bluetooth_qs_tile_dialog") // TODO(b/300995746): Tracking Bug /** A resource flag for whether the communal service is enabled. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index fe9865b2d1dd..53ec3dead6c5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -3401,7 +3401,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } if (mPowerGestureIntercepted && mOccluded && isSecure() - && mUpdateMonitor.isFaceEnrolled()) { + && mUpdateMonitor.isFaceEnabledAndEnrolled()) { flags |= StatusBarManager.DISABLE_RECENT; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index c4dfe9afeb2a..8ef26621362f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -69,7 +69,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull @@ -89,7 +88,7 @@ import kotlinx.coroutines.withContext */ interface DeviceEntryFaceAuthRepository { /** Provide the current face authentication state for device entry. */ - val isAuthenticated: Flow<Boolean> + val isAuthenticated: StateFlow<Boolean> /** Whether face auth can run at this point. */ val canRunFaceAuth: StateFlow<Boolean> @@ -199,8 +198,7 @@ constructor( private val canRunDetection: StateFlow<Boolean> private val _isAuthenticated = MutableStateFlow(false) - override val isAuthenticated: Flow<Boolean> - get() = _isAuthenticated + override val isAuthenticated: StateFlow<Boolean> = _isAuthenticated private var cancellationInProgress = MutableStateFlow(false) @@ -243,61 +241,52 @@ constructor( .collect(Collectors.toSet()) dumpManager.registerCriticalDumpable("DeviceEntryFaceAuthRepositoryImpl", this) - if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) { - canRunFaceAuth = - listOf( - *gatingConditionsForAuthAndDetect(), - Pair(isLockedOut.isFalse(), "isNotInLockOutState"), - Pair( - trustRepository.isCurrentUserTrusted.isFalse(), - "currentUserIsNotTrusted" - ), - Pair( - biometricSettingsRepository.isFaceAuthCurrentlyAllowed, - "isFaceAuthCurrentlyAllowed" - ), - Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"), - ) - .andAllFlows("canFaceAuthRun", faceAuthLog) - .flowOn(mainDispatcher) - .stateIn(applicationScope, SharingStarted.Eagerly, false) - - // Face detection can run only when lockscreen bypass is enabled - // & detection is supported - // & biometric unlock is not allowed - // or user is trusted by trust manager & we want to run face detect to dismiss - // keyguard - canRunDetection = - listOf( - *gatingConditionsForAuthAndDetect(), - Pair(isBypassEnabled, "isBypassEnabled"), - Pair( - biometricSettingsRepository.isFaceAuthCurrentlyAllowed - .isFalse() - .or(trustRepository.isCurrentUserTrusted), - "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted" - ), - // We don't want to run face detect if fingerprint can be used to unlock the - // device - // but it's not possible to authenticate with FP from the bouncer (UDFPS) - Pair( - and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning) - .isFalse(), - "udfpsAuthIsNotPossibleAnymore" - ) + canRunFaceAuth = + listOf( + *gatingConditionsForAuthAndDetect(), + Pair(isLockedOut.isFalse(), "isNotInLockOutState"), + Pair(trustRepository.isCurrentUserTrusted.isFalse(), "currentUserIsNotTrusted"), + Pair( + biometricSettingsRepository.isFaceAuthCurrentlyAllowed, + "isFaceAuthCurrentlyAllowed" + ), + Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"), + ) + .andAllFlows("canFaceAuthRun", faceAuthLog) + .flowOn(mainDispatcher) + .stateIn(applicationScope, SharingStarted.Eagerly, false) + + // Face detection can run only when lockscreen bypass is enabled + // & detection is supported + // & biometric unlock is not allowed + // or user is trusted by trust manager & we want to run face detect to dismiss + // keyguard + canRunDetection = + listOf( + *gatingConditionsForAuthAndDetect(), + Pair(isBypassEnabled, "isBypassEnabled"), + Pair( + biometricSettingsRepository.isFaceAuthCurrentlyAllowed + .isFalse() + .or(trustRepository.isCurrentUserTrusted), + "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted" + ), + // We don't want to run face detect if fingerprint can be used to unlock the + // device + // but it's not possible to authenticate with FP from the bouncer (UDFPS) + Pair( + and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(), + "udfpsAuthIsNotPossibleAnymore" ) - .andAllFlows("canFaceDetectRun", faceDetectLog) - .flowOn(mainDispatcher) - .stateIn(applicationScope, SharingStarted.Eagerly, false) - observeFaceAuthGatingChecks() - observeFaceDetectGatingChecks() - observeFaceAuthResettingConditions() - listenForSchedulingWatchdog() - processPendingAuthRequests() - } else { - canRunFaceAuth = MutableStateFlow(false).asStateFlow() - canRunDetection = MutableStateFlow(false).asStateFlow() - } + ) + .andAllFlows("canFaceDetectRun", faceDetectLog) + .flowOn(mainDispatcher) + .stateIn(applicationScope, SharingStarted.Eagerly, false) + observeFaceAuthGatingChecks() + observeFaceDetectGatingChecks() + observeFaceAuthResettingConditions() + listenForSchedulingWatchdog() + processPendingAuthRequests() } private fun listenForSchedulingWatchdog() { @@ -454,8 +443,8 @@ constructor( if (errorStatus.isLockoutError()) { _isLockedOut.value = true } - _authenticationStatus.value = errorStatus _isAuthenticated.value = false + _authenticationStatus.value = errorStatus if (errorStatus.isHardwareError()) { faceAuthLogger.hardwareError(errorStatus) handleFaceHardwareError() @@ -477,8 +466,17 @@ constructor( } override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) { - _authenticationStatus.value = SuccessFaceAuthenticationStatus(result) + // Update _isAuthenticated before _authenticationStatus is updated. There are + // consumers that receive the face authentication updates through a long chain of + // callbacks + // _authenticationStatus -> KeyguardUpdateMonitor -> KeyguardStateController -> + // onUnlockChanged + // These consumers then query the isAuthenticated boolean. This makes sure that the + // boolean is updated to new value before the event is propagated. + // TODO (b/310592822): once all consumers can use the new system directly, we don't + // have to worry about this ordering. _isAuthenticated.value = true + _authenticationStatus.value = SuccessFaceAuthenticationStatus(result) faceAuthLogger.faceAuthSuccess(result) onFaceAuthRequestCompleted() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt index c8cb9e6aa0dd..f4a74f03696c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt @@ -24,6 +24,7 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow /** @@ -34,11 +35,9 @@ import kotlinx.coroutines.flow.emptyFlow */ @SysUISingleton class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceAuthRepository { - override val isAuthenticated: Flow<Boolean> - get() = emptyFlow() + override val isAuthenticated: StateFlow<Boolean> = MutableStateFlow(false) - private val _canRunFaceAuth = MutableStateFlow(false) - override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth + override val canRunFaceAuth: StateFlow<Boolean> = MutableStateFlow(false) override val authenticationStatus: Flow<FaceAuthenticationStatus> get() = emptyFlow() @@ -46,11 +45,9 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA override val detectionStatus: Flow<FaceDetectionStatus> get() = emptyFlow() - private val _isLockedOut = MutableStateFlow(false) - override val isLockedOut: StateFlow<Boolean> = _isLockedOut + override val isLockedOut: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow() - private val _isAuthRunning = MutableStateFlow(false) - override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning + override val isAuthRunning: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow() override val isBypassEnabled: Flow<Boolean> get() = emptyFlow() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt index 85b0f4fb864b..5ed70b526f1b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt @@ -44,6 +44,8 @@ interface KeyguardFaceAuthInteractor { /** Whether face auth is enrolled and enabled for the current user */ fun isFaceAuthEnabledAndEnrolled(): Boolean + /** Whether the current user is authenticated successfully with face auth */ + fun isAuthenticated(): Boolean /** * Register listener for use from code that cannot use [authenticationStatus] or * [detectionStatus] @@ -53,9 +55,6 @@ interface KeyguardFaceAuthInteractor { /** Unregister previously registered listener */ fun unregisterListener(listener: FaceAuthenticationListener) - /** Whether the face auth interactor is enabled or not. */ - fun isEnabled(): Boolean - fun onUdfpsSensorTouched() fun onAssistantTriggeredOnLockScreen() fun onDeviceLifted() @@ -65,6 +64,9 @@ interface KeyguardFaceAuthInteractor { fun onPrimaryBouncerUserInput() fun onAccessibilityAction() fun onWalletLaunched() + + /** Whether face auth is considered class 3 */ + fun isFaceAuthStrong(): Boolean } /** @@ -81,4 +83,10 @@ interface FaceAuthenticationListener { /** Receive status updates whenever face detection runs */ fun onDetectionStatusChanged(status: FaceDetectionStatus) + + fun onLockoutStateChanged(isLockedOut: Boolean) + + fun onRunningStateChanged(isRunning: Boolean) + + fun onAuthEnrollmentStateChanged(enrolled: Boolean) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 949c940bdebc..91b671599eb0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -31,8 +31,6 @@ import com.android.systemui.common.shared.model.Position import com.android.systemui.common.shared.model.SharedNotificationContainerPosition import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel @@ -76,8 +74,7 @@ class KeyguardInteractor constructor( private val repository: KeyguardRepository, private val commandQueue: CommandQueue, - private val powerInteractor: PowerInteractor, - featureFlags: FeatureFlags, + powerInteractor: PowerInteractor, sceneContainerFlags: SceneContainerFlags, bouncerRepository: KeyguardBouncerRepository, configurationRepository: ConfigurationRepository, @@ -197,22 +194,18 @@ constructor( /** Whether camera is launched over keyguard. */ val isSecureCameraActive: Flow<Boolean> by lazy { - if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) { - combine( - isKeyguardVisible, - primaryBouncerShowing, - onCameraLaunchDetected, - ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent -> - when { - isKeyguardVisible -> false - isPrimaryBouncerShowing -> false - else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP - } + combine( + isKeyguardVisible, + primaryBouncerShowing, + onCameraLaunchDetected, + ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent -> + when { + isKeyguardVisible -> false + isPrimaryBouncerShowing -> false + else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP } - .onStart { emit(false) } - } else { - flowOf(false) - } + } + .onStart { emit(false) } } /** The approximate location on the screen of the fingerprint sensor, if one is available. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt index fbadde63a6b9..cd6ab31f3908 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt @@ -42,9 +42,12 @@ class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInt override fun isLockedOut(): Boolean = false - override fun isEnabled() = false override fun isFaceAuthEnabledAndEnrolled(): Boolean = false + override fun isFaceAuthStrong(): Boolean = false + + override fun isAuthenticated(): Boolean = false + override fun registerListener(listener: FaceAuthenticationListener) {} override fun unregisterListener(listener: FaceAuthenticationListener) {} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt index 2641846251cc..ae356cd94350 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -30,8 +30,6 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository @@ -45,6 +43,7 @@ import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample +import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -71,10 +70,9 @@ constructor( @Application private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, private val repository: DeviceEntryFaceAuthRepository, - private val primaryBouncerInteractor: PrimaryBouncerInteractor, + private val primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor>, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, - private val featureFlags: FeatureFlags, private val faceAuthenticationLogger: FaceAuthenticationLogger, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, @@ -88,16 +86,16 @@ constructor( private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf() override fun start() { - if (!isEnabled()) { - return - } - // This is required because fingerprint state required for the face auth repository is - // backed by KeyguardUpdateMonitor. KeyguardUpdateMonitor constructor accesses the biometric - // state which makes lazy injection not an option. + // Todo(b/310594096): there is a dependency cycle introduced by the repository depending on + // KeyguardBypassController, which in turn depends on KeyguardUpdateMonitor through + // its other dependencies. Once bypassEnabled state is available through a repository, we + // can break that cycle and inject this interactor directly into KeyguardUpdateMonitor keyguardUpdateMonitor.setFaceAuthInteractor(this) observeFaceAuthStateUpdates() faceAuthenticationLogger.interactorStarted() - primaryBouncerInteractor.isShowing + primaryBouncerInteractor + .get() + .isShowing .whenItFlipsToTrue() .onEach { faceAuthenticationLogger.bouncerVisibilityChanged() @@ -176,7 +174,7 @@ constructor( FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING, // Fallback to detection if bouncer is not showing so that we can detect a // face and then show the bouncer to the user if face auth can't run - fallbackToDetect = !primaryBouncerInteractor.isBouncerShowing() + fallbackToDetect = !primaryBouncerInteractor.get().isBouncerShowing() ) } } @@ -231,9 +229,8 @@ constructor( override fun canFaceAuthRun(): Boolean = repository.canRunFaceAuth.value - override fun isEnabled(): Boolean { - return featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR) - } + override fun isFaceAuthStrong(): Boolean = + facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG override fun onPrimaryBouncerUserInput() { repository.cancel() @@ -248,29 +245,24 @@ constructor( override val detectionStatus = repository.detectionStatus private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) { - if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) { - if (repository.isLockedOut.value) { - faceAuthenticationStatusOverride.value = - ErrorFaceAuthenticationStatus( - BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT, - context.resources.getString(R.string.keyguard_face_unlock_unavailable) - ) - } else { - faceAuthenticationStatusOverride.value = null - faceAuthenticationLogger.authRequested(uiEvent) - repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect) - } + if (repository.isLockedOut.value) { + faceAuthenticationStatusOverride.value = + ErrorFaceAuthenticationStatus( + BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT, + context.resources.getString(R.string.keyguard_face_unlock_unavailable) + ) } else { - faceAuthenticationLogger.ignoredFaceAuthTrigger( - uiEvent, - ignoredReason = "Skipping face auth request because feature flag is false" - ) + faceAuthenticationStatusOverride.value = null + faceAuthenticationLogger.authRequested(uiEvent) + repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect) } } override fun isFaceAuthEnabledAndEnrolled(): Boolean = biometricSettingsRepository.isFaceAuthEnrolledAndEnabled.value + override fun isAuthenticated(): Boolean = repository.isAuthenticated.value + private fun observeFaceAuthStateUpdates() { authenticationStatus .onEach { authStatusUpdate -> @@ -284,6 +276,21 @@ constructor( } .flowOn(mainDispatcher) .launchIn(applicationScope) + repository.isLockedOut + .onEach { lockedOut -> listeners.forEach { it.onLockoutStateChanged(lockedOut) } } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + repository.isAuthRunning + .onEach { running -> listeners.forEach { it.onRunningStateChanged(running) } } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled + .onEach { enrolledAndEnabled -> + listeners.forEach { it.onAuthEnrollmentStateChanged(enrolledAndEnabled) } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt index 99529a100b07..9a50d8370525 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt @@ -31,6 +31,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart @@ -49,12 +50,22 @@ constructor( transitionInteractor.startedKeyguardState.map { keyguardState -> keyguardState == KeyguardState.AOD } + + private fun getColor(usingBackgroundProtection: Boolean): Int { + return if (usingBackgroundProtection) { + Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) + } else { + Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent) + } + } + private val color: Flow<Int> = - configurationRepository.onAnyConfigurationChange - .map { Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) } - .onStart { - emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)) - } + deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBgProtection -> + configurationRepository.onAnyConfigurationChange + .map { getColor(useBgProtection) } + .onStart { emit(getColor(useBgProtection)) } + } + private val useAodIconVariant: Flow<Boolean> = combine(isShowingAod, deviceEntryUdfpsInteractor.isUdfpsSupported) { isTransitionToAod, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 315626b6fcae..b3c7d3790527 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -25,7 +25,6 @@ import com.android.systemui.Flags.newAodTransition import com.android.systemui.common.shared.model.SharedNotificationContainerPosition import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor -import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.keyguard.domain.interactor.BurnInInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -67,7 +66,6 @@ constructor( private val context: Context, private val deviceEntryInteractor: DeviceEntryInteractor, private val dozeParameters: DozeParameters, - private val featureFlags: FeatureFlagsClassic, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor, diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt index f2db088ced83..44232ffb3ad7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt @@ -19,12 +19,18 @@ package com.android.systemui.media.controls.util import android.app.StatusBarManager import android.os.UserHandle import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags +import com.android.systemui.scene.shared.flag.SceneContainerFlags import javax.inject.Inject @SysUISingleton -class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { +class MediaFlags +@Inject +constructor( + private val featureFlags: FeatureFlagsClassic, + private val sceneContainerFlags: SceneContainerFlags +) { /** * Check whether media control actions should be based on PlaybackState instead of notification */ @@ -48,4 +54,8 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { /** Check whether we allow remote media to generate resume controls */ fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME) + + /** Check whether to use flexiglass layout */ + fun isFlexiglassEnabled() = + sceneContainerFlags.isEnabled() && MediaInSceneContainerFlag.isEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt new file mode 100644 index 000000000000..898298c58b32 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 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.controls.util + +import com.android.systemui.Flags +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the media_in_scene_container flag state. */ +@Suppress("NOTHING_TO_INLINE") +object MediaInSceneContainerFlag { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_MEDIA_IN_SCENE_CONTAINER + + /** Is the flag enabled? */ + @JvmStatic + inline val isEnabled + get() = Flags.mediaInSceneContainer() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 58c4f0d029c7..ef7296743f20 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -131,6 +131,9 @@ class EmojiHelper { + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})" + "?)*"; + // Not all JDKs support emoji patterns, including the one errorprone runs under, which + // makes it think that this is an invalid pattern. + @SuppressWarnings("InvalidPatternSyntax") static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt index 346d5c30a63e..e5e1e8445e94 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.qs.ui.adapter.QSSceneAdapter import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.SceneKey @@ -33,14 +32,10 @@ import kotlinx.coroutines.flow.map class QuickSettingsSceneViewModel @Inject constructor( - private val deviceEntryInteractor: DeviceEntryInteractor, val shadeHeaderViewModel: ShadeHeaderViewModel, val qsSceneAdapter: QSSceneAdapter, val notifications: NotificationsPlaceholderViewModel, ) { - /** Notifies that some content in quick settings was clicked. */ - fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry() - val destinationScenes = qsSceneAdapter.isCustomizing.map { customizing -> if (customizing) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index c810786681f0..c06e9a443a7b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -98,7 +98,6 @@ import com.android.internal.policy.SystemBarUtils; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; import com.android.keyguard.ActiveUnlockConfig; -import com.android.keyguard.FaceAuthApiRequestReason; import com.android.keyguard.KeyguardClockSwitch.ClockSize; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; @@ -2966,10 +2965,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Try triggering face auth, this "might" run. Check // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run. mKeyguardFaceAuthInteractor.onNotificationPanelClicked(); - boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth( - FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED); - if (didFaceAuthRun) { + if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) { mUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT, "lockScreenEmptySpaceTap"); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index e84bfc512eb5..dd194eaade9b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -61,7 +61,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.SystemBarUtils; -import com.android.keyguard.FaceAuthApiRequestReason; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; @@ -981,7 +980,6 @@ public class QuickSettingsController implements Dumpable { // this will speed up notification actions. if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) { mKeyguardFaceAuthInteractor.onQsExpansionStared(); - mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.QS_EXPANDED); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index dd24ca78005f..08415cb5b0cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -1033,7 +1033,7 @@ public class KeyguardIndicationController { if (mStatusBarKeyguardViewManager.isBouncerShowing()) { if (mAlternateBouncerInteractor.isVisibleState()) { return; // udfps affordance is highlighted, no need to show action to unlock - } else if (mKeyguardUpdateMonitor.isFaceEnrolled() + } else if (mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled() && !mKeyguardUpdateMonitor.getIsFaceAuthenticated()) { String message; if (mAccessibilityManager.isEnabled() @@ -1215,7 +1215,7 @@ public class KeyguardIndicationController { mContext.getString(R.string.keyguard_suggest_fingerprint) ); } else if (fpAuthFailed - && mKeyguardUpdateMonitor.getUserUnlockedWithFace(getCurrentUser())) { + && mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()) { // face had already previously unlocked the device, so instead of showing a // fingerprint error, tell them they have already unlocked with face auth // and how to enter their device diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt index 747efe3fb2c9..933d0ab880bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt @@ -36,7 +36,8 @@ enum class StatusBarMode { /** * A mode where notification icons in the status bar are hidden and replaced by a dot (this mode * can be requested by apps). See - * [com.android.systemui.statusbar.phone.LightsOutNotifController]. + * [com.android.systemui.statusbar.phone.LegacyLightsOutNotifController] and + * [com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor]. */ LIGHTS_OUT, /** Similar to [LIGHTS_OUT], but also with a transparent background for the status bar. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt index d1594ef2e404..04152123e42d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.merge /** * Repository for data that's specific to the status bar **on keyguard**. For data that applies to - * all status bars, use [StatusBarModeRepository]. + * all status bars, use [StatusBarModeRepositoryStore]. */ interface KeyguardStatusBarRepository { /** True if we can show the user switcher on keyguard and false otherwise. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt index 47994d92d22b..6429815bcb9f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt @@ -25,10 +25,8 @@ import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BA import android.view.WindowInsetsController.Appearance import com.android.internal.statusbar.LetterboxDetails import com.android.internal.view.AppearanceRegion -import com.android.systemui.CoreStartable -import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener import com.android.systemui.statusbar.data.model.StatusBarAppearance @@ -38,13 +36,10 @@ import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator import com.android.systemui.statusbar.phone.StatusBarBoundsProvider import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository -import dagger.Binds -import dagger.Module -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap -import dagger.multibindings.IntoSet +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.io.PrintWriter -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -61,7 +56,7 @@ import kotlinx.coroutines.flow.stateIn * Note: These status bar modes are status bar *window* states that are sent to us from * WindowManager, not determined internally. */ -interface StatusBarModeRepository { +interface StatusBarModePerDisplayRepository { /** * True if the status bar window is showing transiently and will disappear soon, and false * otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR @@ -108,16 +103,15 @@ interface StatusBarModeRepository { fun clearTransient() } -@SysUISingleton -class StatusBarModeRepositoryImpl -@Inject +class StatusBarModePerDisplayRepositoryImpl +@AssistedInject constructor( @Application scope: CoroutineScope, - @DisplayId thisDisplayId: Int, + @Assisted("displayId") thisDisplayId: Int, private val commandQueue: CommandQueue, private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator, ongoingCallRepository: OngoingCallRepository, -) : StatusBarModeRepository, CoreStartable, OnStatusBarViewInitializedListener { +) : StatusBarModePerDisplayRepository, OnStatusBarViewInitializedListener, Dumpable { private val commandQueueCallback = object : CommandQueue.Callbacks { @@ -166,7 +160,7 @@ constructor( } } - override fun start() { + fun start() { commandQueue.addCallback(commandQueueCallback) } @@ -340,16 +334,7 @@ constructor( ) } -@Module -interface StatusBarModeRepositoryModule { - @Binds fun bindImpl(impl: StatusBarModeRepositoryImpl): StatusBarModeRepository - - @Binds - @IntoMap - @ClassKey(StatusBarModeRepositoryImpl::class) - fun bindCoreStartable(impl: StatusBarModeRepositoryImpl): CoreStartable - - @Binds - @IntoSet - fun bindViewInitListener(impl: StatusBarModeRepositoryImpl): OnStatusBarViewInitializedListener +@AssistedFactory +interface StatusBarModePerDisplayRepositoryFactory { + fun create(@Assisted("displayId") displayId: Int): StatusBarModePerDisplayRepositoryImpl } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt new file mode 100644 index 000000000000..962cb0953f97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.DisplayId +import com.android.systemui.statusbar.core.StatusBarInitializer +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import dagger.multibindings.IntoSet +import java.io.PrintWriter +import javax.inject.Inject + +interface StatusBarModeRepositoryStore { + val defaultDisplay: StatusBarModePerDisplayRepository + fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository +} + +@SysUISingleton +class StatusBarModeRepositoryImpl +@Inject +constructor( + @DisplayId private val displayId: Int, + factory: StatusBarModePerDisplayRepositoryFactory +) : + StatusBarModeRepositoryStore, + CoreStartable, + StatusBarInitializer.OnStatusBarViewInitializedListener { + override val defaultDisplay = factory.create(displayId) + + override fun forDisplay(displayId: Int) = + if (this.displayId == displayId) { + defaultDisplay + } else { + TODO("b/127878649 implement multi-display state management") + } + + override fun start() { + defaultDisplay.start() + } + + override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) { + defaultDisplay.onStatusBarViewInitialized(component) + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + defaultDisplay.dump(pw, args) + } +} + +@Module +interface StatusBarModeRepositoryModule { + @Binds fun bindImpl(impl: StatusBarModeRepositoryImpl): StatusBarModeRepositoryStore + + @Binds + @IntoMap + @ClassKey(StatusBarModeRepositoryStore::class) + fun bindCoreStartable(impl: StatusBarModeRepositoryImpl): CoreStartable + + @Binds + @IntoSet + fun bindViewInitListener( + impl: StatusBarModeRepositoryImpl + ): StatusBarInitializer.OnStatusBarViewInitializedListener +} 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 11c65e542bcd..6cb079a22e7d 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 @@ -2221,8 +2221,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mMenuRow != null) { mMenuRow.resetMenu(); } - mTranslateAnim = null; } + mTranslateAnim = null; } }); mTranslateAnim = translateAnim; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index 8eda96f62257..64f61d9ac2da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -27,6 +27,7 @@ import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -42,9 +43,11 @@ import java.util.Arrays; * A view that can be used for both the dimmed and normal background of an notification. */ public class NotificationBackgroundView extends View implements Dumpable { + private static final String TAG = "NotificationBackgroundView"; private final boolean mDontModifyCorners; private Drawable mBackground; + private Drawable mBackgroundDrawableToTint; private int mClipTopAmount; private int mClipBottomAmount; private int mTintColor; @@ -131,6 +134,7 @@ public class NotificationBackgroundView extends View implements Dumpable { unscheduleDrawable(mBackground); } mBackground = background; + mBackgroundDrawableToTint = findBackgroundDrawableToTint(mBackground); mRippleColor = null; mBackground.mutate(); if (mBackground != null) { @@ -144,25 +148,46 @@ public class NotificationBackgroundView extends View implements Dumpable { invalidate(); } + // setCustomBackground should be called from ActivatableNotificationView.initBackground + // with R.drawable.notification_material_bg, which is a layer-list with a lower layer + // for the background color (annotated with an ID so we can find it) and an upper layer + // to blend in the stateful @color/notification_overlay_color. + // + // If the notification is tinted, we want to set a tint list on *just that lower layer* that + // will replace the default materialColorSurfaceContainerHigh *without* wiping out the stateful + // tints in the upper layer that make the hovered and pressed states visible. + // + // This function fishes that lower layer out, or makes a fuss in logcat if it can't find it. + private @Nullable Drawable findBackgroundDrawableToTint(@Nullable Drawable background) { + if (background == null) { + return null; + } + + if (!(background instanceof LayerDrawable)) { + Log.wtf(TAG, "background is not a LayerDrawable: " + background); + return background; + } + + final Drawable backgroundColorLayer = ((LayerDrawable) background).findDrawableByLayerId( + R.id.notification_background_color_layer); + + if (backgroundColorLayer == null) { + Log.wtf(TAG, "background is missing background color layer: " + background); + return background; + } + + return backgroundColorLayer; + } + public void setCustomBackground(int drawableResId) { final Drawable d = mContext.getDrawable(drawableResId); setCustomBackground(d); } public void setTint(int tintColor) { - if (tintColor != 0) { - ColorStateList stateList = new ColorStateList(new int[][]{ - new int[]{com.android.internal.R.attr.state_pressed}, - new int[]{com.android.internal.R.attr.state_hovered}, - new int[]{}}, - - new int[]{tintColor, tintColor, tintColor} - ); - mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP); - mBackground.setTintList(stateList); - } else { - mBackground.setTintList(null); - } + mBackgroundDrawableToTint.setTint(tintColor); + mBackgroundDrawableToTint.setTintMode(PorterDuff.Mode.SRC_ATOP); + mTintColor = tintColor; invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataStoreRefactor.kt index 44387c225ef1..8fc7106ecada 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataStoreRefactor.kt @@ -50,4 +50,4 @@ object NotificationsLiveDataStoreRefactor { */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) -}
\ No newline at end of file +} 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 283a5930f930..6944453506a8 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 @@ -565,6 +565,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; private final ScreenOffAnimationController mScreenOffAnimationController; private boolean mShouldUseSplitNotificationShade; + private boolean mShouldSkipTopPaddingAnimationAfterFold = false; private boolean mHasFilteredOutSeenNotifications; @Nullable private SplitShadeStateController mSplitShadeStateController = null; private boolean mIsSmallLandscapeLockscreenEnabled = false; @@ -1364,7 +1365,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mTopPadding = topPadding; updateAlgorithmHeightAndPadding(); updateContentHeight(); - if (shouldAnimate && mAnimationsEnabled && mIsExpanded) { + if (mAmbientState.isOnKeyguard() + && !mShouldUseSplitNotificationShade + && mShouldSkipTopPaddingAnimationAfterFold) { + mShouldSkipTopPaddingAnimationAfterFold = false; + } else if (shouldAnimate && mAnimationsEnabled && mIsExpanded) { mTopPaddingNeedsAnimation = true; mNeedsAnimation = true; } @@ -3751,6 +3756,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } protected boolean isInsideQsHeader(MotionEvent ev) { + if (mQsHeader == null) { + Log.wtf(TAG, "qsHeader is null while NSSL is handling a touch"); + return false; + } + mQsHeader.getBoundsOnScreen(mQsHeaderBound); /** * One-handed mode defines a feature FEATURE_ONE_HANDED of DisplayArea {@link DisplayArea} @@ -5736,6 +5746,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable boolean split = mSplitShadeStateController.shouldUseSplitNotificationShade(getResources()); if (split != mShouldUseSplitNotificationShade) { mShouldUseSplitNotificationShade = split; + mShouldSkipTopPaddingAnimationAfterFold = true; mAmbientState.setUseSplitShade(split); updateDismissBehavior(); updateUseRoundedRectClipping(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index e1fba2eda1c4..7aa7976b8f92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -372,26 +372,6 @@ constructor( ) } - override fun executeRunnableDismissingKeyguard( - runnable: Runnable?, - cancelAction: Runnable?, - dismissShade: Boolean, - afterKeyguardGone: Boolean, - deferred: Boolean, - willAnimateOnKeyguard: Boolean, - customMessage: String?, - ) { - activityStarterInternal.executeRunnableDismissingKeyguard( - runnable = runnable, - cancelAction = cancelAction, - dismissShade = dismissShade, - afterKeyguardGone = afterKeyguardGone, - deferred = deferred, - willAnimateOnKeyguard = willAnimateOnKeyguard, - customMessage = customMessage, - ) - } - override fun postQSRunnableDismissingKeyguard(runnable: Runnable?) { postOnUiThread { statusBarStateController.setLeaveOpenOnKeyguardHide(true) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 645769cdd586..57d49b250883 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -33,7 +33,6 @@ import static com.android.systemui.statusbar.NotificationLockscreenUserManager.P import static com.android.systemui.statusbar.StatusBarState.SHADE; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.IWallpaperManager; import android.app.KeyguardManager; @@ -200,7 +199,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.data.model.StatusBarMode; -import com.android.systemui.statusbar.data.repository.StatusBarModeRepository; +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; @@ -388,7 +387,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarInitializer mStatusBarInitializer; private final StatusBarWindowController mStatusBarWindowController; - private final StatusBarModeRepository mStatusBarModeRepository; + private final StatusBarModeRepositoryStore mStatusBarModeRepository; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @VisibleForTesting DozeServiceHost mDozeServiceHost; @@ -606,7 +605,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { StatusBarInitializer statusBarInitializer, StatusBarWindowController statusBarWindowController, StatusBarWindowStateController statusBarWindowStateController, - StatusBarModeRepository statusBarModeRepository, + StatusBarModeRepositoryStore statusBarModeRepository, KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarSignalPolicy statusBarSignalPolicy, PulseExpansionHandler pulseExpansionHandler, @@ -900,7 +899,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { setUpPresenter(); if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) { - mStatusBarModeRepository.showTransient(); + mStatusBarModeRepository.getDefaultDisplay().showTransient(); } mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior, @@ -1147,9 +1146,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mDemoModeController.addCallback(mDemoModeCallback); mJavaAdapter.alwaysCollectFlow( - mStatusBarModeRepository.isTransientShown(), this::onTransientShownChanged); + mStatusBarModeRepository.getDefaultDisplay().isTransientShown(), + this::onTransientShownChanged); mJavaAdapter.alwaysCollectFlow( - mStatusBarModeRepository.getStatusBarMode(), + mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode(), this::updateBarMode); mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get(); @@ -1209,7 +1209,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void hide() { - mStatusBarModeRepository.clearTransient(); + mStatusBarModeRepository.getDefaultDisplay().clearTransient(); } }); @@ -1657,7 +1657,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (mDemoModeController.isInDemoMode()) return; if (mStatusBarTransitions != null) { checkBarMode( - mStatusBarModeRepository.getStatusBarMode().getValue(), + mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(), mStatusBarWindowState, mStatusBarTransitions); } @@ -1668,7 +1668,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { /** Temporarily hides Bubbles if the status bar is hidden. */ @Override public void updateBubblesVisibility() { - StatusBarMode mode = mStatusBarModeRepository.getStatusBarMode().getValue(); + StatusBarMode mode = + mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(); mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged( mode != StatusBarMode.LIGHTS_OUT && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT @@ -2927,45 +2928,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } } - /** - * Dismiss the keyguard then execute an action. - * - * @param action The action to execute after dismissing the keyguard. - * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard. - * @param willAnimateOnKeyguard Whether {@param action} will run an animation on the keyguard if - * we are locked. - */ - private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone, - boolean collapsePanel, boolean willAnimateOnKeyguard) { - if (!mDeviceProvisionedController.isDeviceProvisioned()) return; - - OnDismissAction onDismissAction = new OnDismissAction() { - @Override - public boolean onDismiss() { - new Thread(() -> { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - action.run(); - }).start(); - - return collapsePanel ? mShadeController.collapseShade() : willAnimateOnKeyguard; - } - - @Override - public boolean willRunAnimationOnKeyguard() { - return willAnimateOnKeyguard; - } - }; - mActivityStarter.dismissKeyguardThenExecute(onDismissAction, /* cancel= */ null, - afterKeyguardGone); - } - private void clearNotificationEffects() { try { mBarService.clearNotificationEffects(); @@ -2993,7 +2955,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // End Extra BaseStatusBarMethods. boolean isTransientShown() { - return mStatusBarModeRepository.isTransientShown().getValue(); + return mStatusBarModeRepository.getDefaultDisplay().isTransientShown().getValue(); } private void updateLightRevealScrimVisibility() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index b0183d3fbd40..674f1698d2a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -98,7 +98,7 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr FACE_UNLOCK_BYPASS_NEVER -> false else -> field } - return enabled && mKeyguardStateController.isFaceEnrolled && + return enabled && mKeyguardStateController.isFaceEnrolledAndEnabled && isPostureAllowedForFaceAuth() } private set(value) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index 332984413dde..32b3ac2ad150 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -22,7 +22,6 @@ import android.hardware.Sensor import android.hardware.TriggerEvent import android.hardware.TriggerEventListener import com.android.keyguard.ActiveUnlockConfig -import com.android.keyguard.FaceAuthApiRequestReason import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.CoreStartable @@ -77,9 +76,6 @@ class KeyguardLiftController @Inject constructor( isListening = false updateListeningState() keyguardFaceAuthInteractor.onDeviceLifted() - keyguardUpdateMonitor.requestFaceAuth( - FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED - ) keyguardUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, "KeyguardLiftController") @@ -117,7 +113,8 @@ class KeyguardLiftController @Inject constructor( val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing - val shouldListen = (onKeyguard || bouncerVisible) && keyguardUpdateMonitor.isFaceEnrolled + val isFaceEnabled = keyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() + val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled if (shouldListen != isListening) { isListening = shouldListen diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java index eba7fe09a8af..7c871e183740 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java @@ -36,6 +36,7 @@ import com.android.internal.statusbar.LetterboxDetails; import com.android.internal.view.AppearanceRegion; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; import com.android.systemui.util.ViewController; @@ -51,7 +52,7 @@ import javax.inject.Named; * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}. */ @StatusBarFragmentScope -public class LightsOutNotifController extends ViewController<View> { +public class LegacyLightsOutNotifController extends ViewController<View> { private final CommandQueue mCommandQueue; private final NotifLiveDataStore mNotifDataStore; private final WindowManager mWindowManager; @@ -63,7 +64,7 @@ public class LightsOutNotifController extends ViewController<View> { private int mDisplayId; @Inject - LightsOutNotifController( + LegacyLightsOutNotifController( @Named(LIGHTS_OUT_NOTIF_VIEW) View lightsOutNotifView, WindowManager windowManager, NotifLiveDataStore notifDataStore, @@ -72,7 +73,12 @@ public class LightsOutNotifController extends ViewController<View> { mWindowManager = windowManager; mNotifDataStore = notifDataStore; mCommandQueue = commandQueue; + } + @Override + protected void onInit() { + super.onInit(); + NotificationsLiveDataStoreRefactor.assertInLegacyMode(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index 4d3e2ad4813a..eec617bf91d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -42,7 +42,7 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.data.model.StatusBarAppearance; -import com.android.systemui.statusbar.data.repository.StatusBarModeRepository; +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.Compile; import com.android.systemui.util.kotlin.JavaAdapter; @@ -68,7 +68,7 @@ public class LightBarController implements private final JavaAdapter mJavaAdapter; private final SysuiDarkIconDispatcher mStatusBarIconController; private final BatteryController mBatteryController; - private final StatusBarModeRepository mStatusBarModeRepository; + private final StatusBarModeRepositoryStore mStatusBarModeRepository; private BiometricUnlockController mBiometricUnlockController; private LightBarTransitionsController mNavigationBarController; @@ -126,7 +126,7 @@ public class LightBarController implements DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, - StatusBarModeRepository statusBarModeRepository, + StatusBarModeRepositoryStore statusBarModeRepository, DumpManager dumpManager, DisplayTracker displayTracker) { mJavaAdapter = javaAdapter; @@ -146,7 +146,7 @@ public class LightBarController implements @Override public void start() { mJavaAdapter.alwaysCollectFlow( - mStatusBarModeRepository.getStatusBarAppearance(), + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance(), this::onStatusBarAppearanceChanged); } @@ -476,7 +476,7 @@ public class LightBarController implements private final DarkIconDispatcher mDarkIconDispatcher; private final BatteryController mBatteryController; private final NavigationModeController mNavModeController; - private final StatusBarModeRepository mStatusBarModeRepository; + private final StatusBarModeRepositoryStore mStatusBarModeRepository; private final DumpManager mDumpManager; private final DisplayTracker mDisplayTracker; @@ -486,7 +486,7 @@ public class LightBarController implements DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, - StatusBarModeRepository statusBarModeRepository, + StatusBarModeRepositoryStore statusBarModeRepository, DumpManager dumpManager, DisplayTracker displayTracker) { mJavaAdapter = javaAdapter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 274b50fd79fd..daadedb06187 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1636,13 +1636,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } /** - * Request to authenticate using face. - */ - public void requestFace(boolean request) { - mKeyguardUpdateManager.requestFaceAuthOnOccludingApp(request); - } - - /** * Request to authenticate using the fingerprint sensor. If the fingerprint sensor is udfps, * uses the color provided by udfpsColor for the fingerprint icon. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt new file mode 100644 index 000000000000..ed8b3e8922f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 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.phone.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.data.model.StatusBarMode +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** + * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where status + * bar and navigation icons dim. In this mode, a notification dot appears where the notification + * icons would appear if they would be shown outside of this mode. + * + * This interactor knows whether the device is in [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE]. + */ +@SysUISingleton +class LightsOutInteractor +@Inject +constructor(private val repository: StatusBarModeRepositoryStore) { + + fun isLowProfile(displayId: Int): Flow<Boolean> = + repository.forDisplay(displayId).statusBarMode.map { + when (it) { + StatusBarMode.LIGHTS_OUT, + StatusBarMode.LIGHTS_OUT_TRANSPARENT -> true + else -> false + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 7adc08ca00c0..49880d4475da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -43,7 +43,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; @@ -139,7 +138,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final OngoingCallController mOngoingCallController; private final SystemStatusAnimationScheduler mAnimationScheduler; private final StatusBarLocationPublisher mLocationPublisher; - private final FeatureFlagsClassic mFeatureFlags; private final NotificationIconAreaController mNotificationIconAreaController; private final ShadeExpansionStateManager mShadeExpansionStateManager; private final StatusBarIconController mStatusBarIconController; @@ -228,7 +226,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue StatusBarLocationPublisher locationPublisher, NotificationIconAreaController notificationIconAreaController, ShadeExpansionStateManager shadeExpansionStateManager, - FeatureFlagsClassic featureFlags, StatusBarIconController statusBarIconController, DarkIconManager.Factory darkIconManagerFactory, CollapsedStatusBarViewModel collapsedStatusBarViewModel, @@ -258,7 +255,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mLocationPublisher = locationPublisher; mNotificationIconAreaController = notificationIconAreaController; mShadeExpansionStateManager = shadeExpansionStateManager; - mFeatureFlags = featureFlags; mStatusBarIconController = statusBarIconController; mCollapsedStatusBarViewModel = collapsedStatusBarViewModel; mCollapsedStatusBarViewBinder = collapsedStatusBarViewBinder; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java index 0618abbf00d8..96faa359d43e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java @@ -18,8 +18,9 @@ package com.android.systemui.statusbar.phone.fragment.dagger; import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.RootView; +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.LightsOutNotifController; +import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; @@ -78,7 +79,9 @@ public interface StatusBarFragmentComponent { getBatteryMeterViewController().init(); getHeadsUpAppearanceController().init(); getPhoneStatusBarViewController().init(); - getLightsOutNotifController().init(); + if (!NotificationsLiveDataStoreRefactor.isEnabled()) { + getLegacyLightsOutNotifController().init(); + } getStatusBarDemoMode().init(); } @@ -101,7 +104,7 @@ public interface StatusBarFragmentComponent { /** */ @StatusBarFragmentScope - LightsOutNotifController getLightsOutNotifController(); + LegacyLightsOutNotifController getLegacyLightsOutNotifController(); /** */ @StatusBarFragmentScope diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index b0532ce1817b..0bdd1a5b4d5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -36,7 +36,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.data.repository.StatusBarModeRepository +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -68,7 +68,7 @@ class OngoingCallController @Inject constructor( private val dumpManager: DumpManager, private val statusBarWindowController: StatusBarWindowController, private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler, - private val statusBarModeRepository: StatusBarModeRepository, + private val statusBarModeRepository: StatusBarModeRepositoryStore, ) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable { private var isFullscreen: Boolean = false /** Non-null if there's an active call notification. */ @@ -129,7 +129,7 @@ class OngoingCallController @Inject constructor( dumpManager.registerDumpable(this) notifCollection.addCollectionListener(notifListener) scope.launch { - statusBarModeRepository.isInFullscreenMode.collect { + statusBarModeRepository.defaultDisplay.isInFullscreenMode.collect { isFullscreen = it updateChipClickListener() updateGestureListening() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt index da9c45ad5ada..9c78ab42a14a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.asStateFlow * * This class is used to break a dependency cycle between * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController] and - * [com.android.systemui.statusbar.data.repository.StatusBarModeRepository]. Instead, those two + * [com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore]. Instead, those two * classes both refer to this repository. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index b9b88f4b762c..7d7f49bb8d17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -16,11 +16,15 @@ package com.android.systemui.statusbar.pipeline.shared.ui.binder +import android.animation.Animator +import android.animation.AnimatorListenerAdapter import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel import javax.inject.Inject import kotlinx.coroutines.launch @@ -61,9 +65,49 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa listener.onTransitionFromLockscreenToDreamStarted() } } + + if (NotificationsLiveDataStoreRefactor.isEnabled) { + val displayId = view.display.displayId + val lightsOutView: View = view.requireViewById(R.id.notification_lights_out) + launch { + viewModel.areNotificationsLightsOut(displayId).collect { show -> + animateLightsOutView(lightsOutView, show) + } + } + } } } } + + private fun animateLightsOutView(view: View, visible: Boolean) { + view.animate().cancel() + + val alpha = if (visible) 1f else 0f + val duration = if (visible) 750L else 250L + val visibility = if (visible) View.VISIBLE else View.GONE + + if (visible) { + view.alpha = 0f + view.visibility = View.VISIBLE + } + + view + .animate() + .alpha(alpha) + .setDuration(duration) + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + view.alpha = alpha + view.visibility = visibility + // Unset the listener, otherwise this may persist for + // another view property animation + view.animate().setListener(null) + } + } + ) + .start() + } } /** Listener for various events that may affect the status bar's visibility. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt index 15ab143a7aeb..52a6d8cf0952 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt @@ -20,11 +20,17 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor +import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -48,12 +54,25 @@ interface CollapsedStatusBarViewModel { /** Emits whenever a transition from lockscreen to dream has started. */ val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> + + /** + * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where + * status bar and navigation icons dim. In this mode, a notification dot appears where the + * notification icons would appear if they would be shown outside of this mode. + * + * This flow tells when to show or hide the notification dot in the status bar to indicate + * whether there are notifications when the device is in + * [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE]. + */ + fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> } @SysUISingleton class CollapsedStatusBarViewModelImpl @Inject constructor( + private val lightsOutInteractor: LightsOutInteractor, + private val notificationsInteractor: ActiveNotificationsInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, @Application coroutineScope: CoroutineScope, ) : CollapsedStatusBarViewModel { @@ -69,4 +88,17 @@ constructor( keyguardTransitionInteractor.lockscreenToDreamingTransition .filter { it.transitionState == TransitionState.STARTED } .map {} + + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = + if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) { + emptyFlow() + } else { + combine( + notificationsInteractor.areAnyNotificationsPresent, + lightsOutInteractor.isLowProfile(displayId), + ) { hasNotifications, isLowProfile -> + hasNotifications && isLowProfile + } + .distinctUntilChanged() + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java index 52133ee5b7cd..ad2b070788f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java @@ -130,7 +130,7 @@ public interface KeyguardStateController extends CallbackController<Callback> { /** * If there are faces enrolled and user enabled face auth on keyguard. */ - default boolean isFaceEnrolled() { + default boolean isFaceEnrolledAndEnabled() { return false; } @@ -265,7 +265,7 @@ public interface KeyguardStateController extends CallbackController<Callback> { /** * Triggered when face auth becomes available or unavailable. Value should be queried with - * {@link KeyguardStateController#isFaceEnrolled()}. + * {@link KeyguardStateController#isFaceEnrolledAndEnabled()}. */ default void onFaceEnrolledChanged() {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index 8cc7e7d21fc2..3deb9e7414af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -85,7 +85,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private boolean mTrustManaged; private boolean mTrusted; private boolean mDebugUnlocked = false; - private boolean mFaceEnrolled; + private boolean mFaceEnrolledAndEnabled; private float mDismissAmount = 0f; private boolean mDismissingFromTouch = false; @@ -260,16 +260,16 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked); boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user); boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user); - boolean faceEnrolled = mKeyguardUpdateMonitor.isFaceEnrolled(user); + boolean faceEnabledAndEnrolled = mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled(); boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen || trustManaged != mTrustManaged || mTrusted != trusted - || mFaceEnrolled != faceEnrolled; + || mFaceEnrolledAndEnabled != faceEnabledAndEnrolled; if (changed || updateAlways) { mSecure = secure; mCanDismissLockScreen = canDismissLockScreen; mTrusted = trusted; mTrustManaged = trustManaged; - mFaceEnrolled = faceEnrolled; + mFaceEnrolledAndEnabled = faceEnabledAndEnrolled; mLogger.logKeyguardStateUpdate( mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged); notifyUnlockedChanged(); @@ -290,8 +290,8 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override - public boolean isFaceEnrolled() { - return mFaceEnrolled; + public boolean isFaceEnrolledAndEnabled() { + return mFaceEnrolledAndEnabled; } @Override @@ -416,7 +416,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum pw.println(" mTrustManaged: " + mTrustManaged); pw.println(" mTrusted: " + mTrusted); pw.println(" mDebugUnlocked: " + mDebugUnlocked); - pw.println(" mFaceEnrolled: " + mFaceEnrolled); + pw.println(" mFaceEnrolled: " + mFaceEnrolledAndEnabled); pw.println(" isKeyguardFadingAway: " + isKeyguardFadingAway()); pw.println(" isKeyguardGoingAway: " + isKeyguardGoingAway()); pw.println(" isLaunchTransitionFadingAway: " + isLaunchTransitionFadingAway()); diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 5fc435aae67f..cf76c0d2e696 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -29,7 +29,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.user.data.model.SelectedUserModel @@ -120,7 +119,6 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, private val globalSettings: GlobalSettings, private val tracker: UserTracker, - featureFlags: FeatureFlags, ) : UserRepository { private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> = diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index d65a69c62072..9ee3d220a79b 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -64,13 +64,13 @@ import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; import com.android.settingslib.volume.MediaSessions; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.RingerModeLiveData; @@ -99,6 +99,7 @@ import javax.inject.Inject; @SysUISingleton public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable { private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000; private static final int DYNAMIC_STREAM_START_INDEX = 100; @@ -1339,14 +1340,24 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private boolean showForSession(Token token) { if (mVolumeAdjustmentForRemoteGroupSessions) { + if (DEBUG) { + Log.d(TAG, "Volume adjustment for remote group sessions allowed," + + " showForSession: true"); + } return true; } MediaController ctr = new MediaController(mContext, token); String packageName = ctr.getPackageName(); List<RoutingSessionInfo> sessions = mRouter2Manager.getRoutingSessions(packageName); - + if (DEBUG) { + Log.d(TAG, "Found " + sessions.size() + " routing sessions for package name " + + packageName); + } for (RoutingSessionInfo session : sessions) { + if (DEBUG) { + Log.d(TAG, "Found routingSessionInfo: " + session); + } if (!session.isSystemSession() && session.getVolumeHandling() != MediaRoute2Info.PLAYBACK_VOLUME_FIXED) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index 2132904caa84..5558aa72bb57 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -214,14 +214,12 @@ public class WalletActivity extends ComponentActivity implements Utils.getColorAttrDefaultColor( this, com.android.internal.R.attr.colorAccentPrimary)); mKeyguardFaceAuthInteractor.onWalletLaunched(); - mKeyguardViewManager.requestFace(true); } @Override protected void onPause() { super.onPause(); mKeyguardViewManager.requestFp(false, -1); - mKeyguardViewManager.requestFace(false); } @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt index e429446f66cf..3f76d303898e 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt @@ -261,7 +261,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // GIVEN fingerprint and face are NOT enrolled activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor - `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false) + `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false) `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false) // WHEN unlock intent is allowed when NO biometrics are enrolled (0) @@ -291,7 +291,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // GIVEN fingerprint and face are both enrolled activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor - `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) + `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true) `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true) // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs @@ -314,7 +314,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { ) // WHEN fingerprint ONLY enrolled - `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false) + `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false) `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true) // THEN active unlock triggers allowed on unlock intent @@ -325,7 +325,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { ) // WHEN face ONLY enrolled - `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) + `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true) `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false) // THEN active unlock triggers allowed on unlock intent diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 2e9b7e84344b..b403d1d5fbdc 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -125,10 +125,7 @@ class ClockEventControllerTest : SysuiTestCase() { repository = repository, ) - withDeps.featureFlags.apply { - set(Flags.REGION_SAMPLING, false) - set(Flags.FACE_AUTH_REFACTOR, false) - } + withDeps.featureFlags.apply { set(Flags.REGION_SAMPLING, false) } underTest = ClockEventController( withDeps.keyguardInteractor, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java index 7feab9141da2..adf0adabe24c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -18,7 +18,6 @@ package com.android.keyguard; import static android.view.View.INVISIBLE; -import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.flags.Flags.MIGRATE_CLOCKS_TO_BLUEPRINT; @@ -179,7 +178,6 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mExecutor = new FakeExecutor(new FakeSystemClock()); mFakeFeatureFlags = new FakeFeatureFlags(); - mFakeFeatureFlags.set(FACE_AUTH_REFACTOR, false); mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mFakeFeatureFlags.set(MIGRATE_CLOCKS_TO_BLUEPRINT, false); mController = new KeyguardClockSwitchController( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 225f12536a33..543b2910bbda 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -47,6 +47,7 @@ import com.android.systemui.classifier.FalsingCollector import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.log.SessionTracker @@ -137,6 +138,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback @Mock private lateinit var audioManager: AudioManager @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @Mock private lateinit var postureController: DevicePostureController @@ -257,7 +259,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { telephonyManager, viewMediatorCallback, audioManager, - mock(), + faceAuthInteractor, mock(), { JavaAdapter(sceneTestUtils.testScope.backgroundScope) }, mSelectedUserInteractor, @@ -576,49 +578,12 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { } @Test - fun onSwipeUp_whenFaceDetectionIsNotRunning_initiatesFaceAuth() { + fun onSwipeUp_forwardsItToFaceAuthInteractor() { val registeredSwipeListener = registeredSwipeListener - whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(false) setupGetSecurityView(SecurityMode.Password) registeredSwipeListener.onSwipeUp() - verify(keyguardUpdateMonitor).requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) - } - - @Test - fun onSwipeUp_whenFaceDetectionIsRunning_doesNotInitiateFaceAuth() { - val registeredSwipeListener = registeredSwipeListener - whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(true) - registeredSwipeListener.onSwipeUp() - verify(keyguardUpdateMonitor, never()) - .requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) - } - @Test - fun onSwipeUp_whenFaceDetectionIsTriggered_hidesBouncerMessage() { - val registeredSwipeListener = registeredSwipeListener - whenever( - keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) - ) - .thenReturn(true) - setupGetSecurityView(SecurityMode.Password) - clearInvocations(viewFlipperController) - registeredSwipeListener.onSwipeUp() - viewControllerImmediately - verify(keyguardPasswordViewControllerMock) - .showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true) - } - - @Test - fun onSwipeUp_whenFaceDetectionIsNotTriggered_retainsBouncerMessage() { - val registeredSwipeListener = registeredSwipeListener - whenever( - keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) - ) - .thenReturn(false) - setupGetSecurityView(SecurityMode.Password) - registeredSwipeListener.onSwipeUp() - verify(keyguardPasswordViewControllerMock, never()) - .showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true) + verify(faceAuthInteractor).onSwipeUpOnBouncer() } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java index 146715d26b7d..13fb42ce8c3e 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java @@ -35,6 +35,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.power.data.repository.FakePowerRepository; import com.android.systemui.power.domain.interactor.PowerInteractorFactory; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; @@ -70,6 +71,7 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardClockSwitch mKeyguardClockSwitch; @Mock protected FrameLayout mMediaHostContainer; + @Mock protected KeyguardStatusAreaView mKeyguardStatusAreaView; @Before public void setup() { @@ -109,6 +111,8 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver); when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch); when(mKeyguardTransitionInteractor.getGoneToAodTransition()).thenReturn(emptyFlow()); + when(mKeyguardStatusView.findViewById(R.id.keyguard_status_area)) + .thenReturn(mKeyguardStatusAreaView); } protected void givenViewAttached() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index 948942fbce3a..9c3288b9f93d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static junit.framework.Assert.assertEquals; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; @@ -27,6 +29,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.animation.AnimatorTestRule; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -40,6 +43,7 @@ import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -51,6 +55,9 @@ import java.lang.reflect.Field; @RunWith(AndroidTestingRunner.class) public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControllerBaseTest { + @Rule + public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + @Test public void dozeTimeTick_updatesSlice() { mController.dozeTimeTick(); @@ -230,4 +237,34 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll throw new RuntimeException(e); } } + + @Test + public void statusAreaHeightChange_animatesHeightOutputChange() { + // Init & Capture Layout Listener + mController.onInit(); + mController.onViewAttached(); + + when(mDozeParameters.getAlwaysOn()).thenReturn(true); + ArgumentCaptor<View.OnLayoutChangeListener> captor = + ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); + verify(mKeyguardStatusAreaView).addOnLayoutChangeListener(captor.capture()); + View.OnLayoutChangeListener listener = captor.getValue(); + + // Setup and validate initial height + when(mKeyguardStatusView.getHeight()).thenReturn(200); + when(mKeyguardClockSwitchController.getNotificationIconAreaHeight()).thenReturn(10); + assertEquals(190, mController.getLockscreenHeight()); + + // Trigger Change and validate value unchanged immediately + when(mKeyguardStatusAreaView.getHeight()).thenReturn(100); + when(mKeyguardStatusView.getHeight()).thenReturn(300); // Include child height + listener.onLayoutChange(mKeyguardStatusAreaView, + /* new layout */ 100, 300, 200, 400, + /* old layout */ 100, 300, 200, 300); + assertEquals(190, mController.getLockscreenHeight()); + + // Complete animation, validate height increased + mAnimatorTestRule.advanceTimeBy(200); + assertEquals(290, mController.getLockscreenHeight()); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 2b41e08065d1..1ab634c46de9 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -23,24 +23,15 @@ import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPR import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT; import static android.hardware.biometrics.SensorProperties.STRENGTH_CONVENIENCE; import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; -import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN; -import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; -import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; -import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING; import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; -import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN; @@ -88,12 +79,7 @@ import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; -import android.hardware.display.DisplayManagerGlobal; -import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceManager; -import android.hardware.face.FaceSensorProperties; -import android.hardware.face.FaceSensorPropertiesInternal; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -121,11 +107,6 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.text.TextUtils; -import android.view.Display; -import android.view.DisplayAdjustments; -import android.view.DisplayInfo; - -import androidx.annotation.NonNull; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.internal.jank.InteractionJankMonitor; @@ -143,10 +124,13 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; -import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; +import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus; +import com.android.systemui.keyguard.shared.model.FaceDetectionStatus; +import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; @@ -199,7 +183,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID, TEST_CARRIER_ID, 0); - private static final int FACE_SENSOR_ID = 0; private static final int FINGERPRINT_SENSOR_ID = 1; @Mock @@ -217,8 +200,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Mock private FingerprintManager mFingerprintManager; @Mock - private FaceManager mFaceManager; - @Mock private BiometricManager mBiometricManager; @Mock private PackageManager mPackageManager; @@ -277,19 +258,18 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Mock private IActivityTaskManager mActivityTaskManager; @Mock - private WakefulnessLifecycle mWakefulness; - @Mock private SelectedUserInteractor mSelectedUserInteractor; + @Mock + private KeyguardFaceAuthInteractor mFaceAuthInteractor; + @Captor + private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener; - private List<FaceSensorPropertiesInternal> mFaceSensorProperties; private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties; private final int mCurrentUserId = 100; @Captor private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback> mBiometricEnabledCallbackArgCaptor; - @Captor - private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor; // Direct executor private final Executor mBackgroundExecutor = Runnable::run; @@ -303,14 +283,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig; private IFingerprintAuthenticatorsRegisteredCallback mFingerprintAuthenticatorsRegisteredCallback; - private IFaceAuthenticatorsRegisteredCallback mFaceAuthenticatorsRegisteredCallback; private final InstanceId mKeyguardInstanceId = InstanceId.fakeInstanceId(999); - private FakeDisplayTracker mDisplayTracker; @Before public void setup() throws RemoteException { MockitoAnnotations.initMocks(this); - mDisplayTracker = new FakeDisplayTracker(mContext); when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId); when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true); @@ -358,12 +335,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext); setupBiometrics(mKeyguardUpdateMonitor); + mKeyguardUpdateMonitor.setFaceAuthInteractor(mFaceAuthInteractor); + verify(mFaceAuthInteractor).registerListener(mFaceAuthenticationListener.capture()); } private void setupBiometrics(KeyguardUpdateMonitor keyguardUpdateMonitor) throws RemoteException { captureAuthenticatorsRegisteredCallbacks(); - setupFaceAuth(/* isClass3 */ false); + when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false); setupFingerprintAuth(/* isClass3 */ true); verify(mBiometricManager) @@ -387,12 +366,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void captureAuthenticatorsRegisteredCallbacks() throws RemoteException { - ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> faceCaptor = - ArgumentCaptor.forClass(IFaceAuthenticatorsRegisteredCallback.class); - verify(mFaceManager).addAuthenticatorsRegisteredCallback(faceCaptor.capture()); - mFaceAuthenticatorsRegisteredCallback = faceCaptor.getValue(); - mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties); - ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> fingerprintCaptor = ArgumentCaptor.forClass(IFingerprintAuthenticatorsRegisteredCallback.class); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( @@ -402,16 +375,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { .onAllAuthenticatorsRegistered(mFingerprintSensorProperties); } - private void setupFaceAuth(boolean isClass3) throws RemoteException { - when(mFaceManager.isHardwareDetected()).thenReturn(true); - when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true); - mFaceSensorProperties = - List.of(createFaceSensorProperties(/* supportsFaceDetection = */ false, isClass3)); - when(mFaceManager.getSensorPropertiesInternal()).thenReturn(mFaceSensorProperties); - mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties); - assertEquals(isClass3, mKeyguardUpdateMonitor.isFaceClass3()); - } - private void setupFingerprintAuth(boolean isClass3) throws RemoteException { when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); @@ -442,28 +405,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { true /* resetLockoutRequiresHardwareAuthToken */); } - @NonNull - private FaceSensorPropertiesInternal createFaceSensorProperties( - boolean supportsFaceDetection, boolean isClass3) { - final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); - componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */, - "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, - "00000001" /* serialNumber */, "" /* softwareVersion */)); - componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */, - "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, - "vendor/version/revision" /* softwareVersion */)); - - return new FaceSensorPropertiesInternal( - FACE_SENSOR_ID /* id */, - isClass3 ? STRENGTH_STRONG : STRENGTH_CONVENIENCE, - 1 /* maxTemplatesAllowed */, - componentInfo, - FaceSensorProperties.TYPE_UNKNOWN, - supportsFaceDetection /* supportsFaceDetection */, - true /* supportsSelfIllumination */, - false /* resetLockoutRequiresChallenge */); - } - @After public void tearDown() { if (mMockitoSession != null) { @@ -887,13 +828,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void whenDetectFace_biometricDetectCallback() throws RemoteException { - ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor = - ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class); - - givenDetectFace(); - verify(mFaceManager).detectFace(any(), faceDetectCallbackCaptor.capture(), any()); - faceDetectCallbackCaptor.getValue().onFaceDetected(0, 0, false); + public void whenDetectFace_biometricDetectCallback() { + mFaceAuthenticationListener.getValue().onDetectionStatusChanged( + new FaceDetectionStatus(0, 0, false, 0L)); // THEN verify keyguardUpdateMonitorCallback receives a detect callback // and NO authenticate callbacks @@ -926,40 +863,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void class3FingerprintLockOut_lockOutClass1Face() throws RemoteException { - setupFaceAuth(/* isClass3 */ false); - setupFingerprintAuth(/* isClass3 */ true); - - // GIVEN primary auth is not required by StrongAuthTracker - primaryAuthNotRequiredByStrongAuthTracker(); - - // WHEN fingerprint (class 3) is lock out - fingerprintErrorTemporaryLockOut(); - - // THEN unlocking with face is not allowed - Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - BiometricSourceType.FACE)); - } - - @Test - public void class3FingerprintLockOut_lockOutClass3Face() throws RemoteException { - setupFaceAuth(/* isClass3 */ true); - setupFingerprintAuth(/* isClass3 */ true); - - // GIVEN primary auth is not required by StrongAuthTracker - primaryAuthNotRequiredByStrongAuthTracker(); - - // WHEN fingerprint (class 3) is lock out - fingerprintErrorTemporaryLockOut(); - - // THEN unlocking with face is not allowed - Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - BiometricSourceType.FACE)); - } - - @Test public void class3FaceLockOut_lockOutClass3Fingerprint() throws RemoteException { - setupFaceAuth(/* isClass3 */ true); + when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(true); + when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true); + setupFingerprintAuth(/* isClass3 */ true); // GIVEN primary auth is not required by StrongAuthTracker @@ -975,7 +882,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void class1FaceLockOut_doesNotLockOutClass3Fingerprint() throws RemoteException { - setupFaceAuth(/* isClass3 */ false); + when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false); setupFingerprintAuth(/* isClass3 */ true); // GIVEN primary auth is not required by StrongAuthTracker @@ -1056,162 +963,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testTriesToAuthenticate_whenBouncer() { - setKeyguardBouncerVisibility(true); - verifyFaceAuthenticateCall(); - } - - @Test - public void testNoStartAuthenticate_whenAboutToShowBouncer() { - mKeyguardUpdateMonitor.sendPrimaryBouncerChanged( - /* bouncerIsOrWillBeShowing */ true, /* bouncerFullyShown */ false); - - verifyFaceAuthenticateNeverCalled(); - } - - @Test - public void testTriesToAuthenticate_whenKeyguard() { - keyguardIsVisible(); - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - verifyFaceAuthenticateCall(); - verify(mUiEventLogger).logWithInstanceIdAndPosition( - eq(FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP), - eq(0), - eq(null), - any(), - eq(PowerManager.WAKE_REASON_POWER_BUTTON)); - } - - @Test - public void skipsAuthentication_whenStatusBarShadeLocked() { - mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED); - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - keyguardIsVisible(); - verifyFaceAuthenticateNeverCalled(); - } - - @Test - public void skipsAuthentication_whenStrongAuthRequired_nonBypass() { - lockscreenBypassIsNotAllowed(); - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); - - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - keyguardIsVisible(); - - verifyFaceAuthenticateNeverCalled(); - } - - @Test - public void nofaceDetect_whenStrongAuthRequiredAndBypassUdfpsSupportedAndFpRunning() - throws RemoteException { - // GIVEN bypass is enabled, face detection is supported - lockscreenBypassIsAllowed(); - supportsFaceDetection(); - keyguardIsVisible(); - - // GIVEN udfps is supported and strong auth required for weak biometrics (face) only - givenUdfpsSupported(); - primaryAuthRequiredForWeakBiometricOnly(); // allows class3 fp to run but not class1 face - - // WHEN the device wakes up - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - // THEN face detect and authenticate are NOT triggered - verifyFaceDetectNeverCalled(); - verifyFaceAuthenticateNeverCalled(); - - // THEN biometric help message sent to callback - verify(mTestCallback).onBiometricHelp( - eq(BIOMETRIC_HELP_FACE_NOT_AVAILABLE), anyString(), eq(BiometricSourceType.FACE)); - } - - @Test - public void faceDetect_whenStrongAuthRequiredAndBypass() throws RemoteException { - givenDetectFace(); - - // FACE detect is triggered, not authenticate - verifyFaceDetectCall(); - verifyFaceAuthenticateNeverCalled(); - - // WHEN bouncer becomes visible - setKeyguardBouncerVisibility(true); - clearInvocations(mFaceManager); - - // THEN face scanning is not run - mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); - verifyFaceAuthenticateNeverCalled(); - verifyFaceDetectNeverCalled(); - } - - @Test - public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() { - // GIVEN bypass is enabled, face detection is NOT supported and strong auth is required - lockscreenBypassIsAllowed(); - primaryAuthRequiredEncrypted(); - keyguardIsVisible(); - - // WHEN the device wakes up - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - // FACE detect and authenticate are NOT triggered - verifyFaceDetectNeverCalled(); - verifyFaceAuthenticateNeverCalled(); - } - - @Test - public void requestFaceAuth_whenFaceAuthWasStarted_returnsTrue() throws RemoteException { - // This satisfies all the preconditions to run face auth. - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - bouncerFullyVisibleAndNotGoingToSleep(); - mTestableLooper.processAllMessages(); - - boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth( - NOTIFICATION_PANEL_CLICKED); - - assertThat(didFaceAuthRun).isTrue(); - } - - @Test - public void requestFaceAuth_whenFaceAuthWasNotStarted_returnsFalse() throws RemoteException { - // This ensures face auth won't run. - biometricsDisabledForCurrentUser(); - mTestableLooper.processAllMessages(); - - boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth( - NOTIFICATION_PANEL_CLICKED); - - assertThat(didFaceAuthRun).isFalse(); - } - - @Test - public void testTriesToAuthenticate_whenAssistant() { - mKeyguardUpdateMonitor.setKeyguardShowing(true, true); - mKeyguardUpdateMonitor.setAssistantVisible(true); - - verifyFaceAuthenticateCall(); - } - - @Test - public void doesNotTryToAuthenticateWhenKeyguardIsNotShowingButOccluded_whenAssistant() { - mKeyguardUpdateMonitor.setKeyguardShowing(false, true); - mKeyguardUpdateMonitor.setAssistantVisible(true); - - verifyFaceAuthenticateNeverCalled(); - } - - @Test public void noFpListeningWhenKeyguardIsOccluded_unlessAlternateBouncerShowing() { // GIVEN device is awake but occluded mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); @@ -1257,62 +1008,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() { - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - lockscreenBypassIsAllowed(); - mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, - mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */, - new ArrayList<>()); - keyguardIsVisible(); - verifyFaceAuthenticateCall(); - } - - @Test - public void faceUnlockDoesNotRunWhenDeviceIsGoingToSleepWithAssistantVisible() { - mKeyguardUpdateMonitor.setKeyguardShowing(true, true); - mKeyguardUpdateMonitor.setAssistantVisible(true); - - verifyFaceAuthenticateCall(); - mTestableLooper.processAllMessages(); - clearInvocations(mFaceManager); - - // Device going to sleep while assistant is visible - mKeyguardUpdateMonitor.handleStartedGoingToSleep(0); - mKeyguardUpdateMonitor.handleFinishedGoingToSleep(0); - mTestableLooper.moveTimeForward(DEFAULT_CANCEL_SIGNAL_TIMEOUT); - mTestableLooper.processAllMessages(); - - mKeyguardUpdateMonitor.handleKeyguardReset(); - - assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse(); - verifyFaceAuthenticateNeverCalled(); - } - - @Test - public void testIgnoresAuth_whenTrustAgentOnKeyguard_withoutBypass() { - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, - mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */, new ArrayList<>()); - keyguardIsVisible(); - verifyFaceAuthenticateNeverCalled(); - } - - @Test - public void testNoFaceAuth_whenLockDown() { - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); - userDeviceLockDown(); - - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - keyguardIsVisible(); - mTestableLooper.processAllMessages(); - - verifyFaceAuthenticateNeverCalled(); - verifyFaceDetectNeverCalled(); - } - - @Test public void testFingerprintPowerPressed_restartsFingerprintListeningStateWithDelay() { mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback .onAuthenticationError(FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, ""); @@ -1329,18 +1024,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() { - // test whether face will be skipped if authenticated, so the value of isClass3Biometric - // doesn't matter here - mKeyguardUpdateMonitor.onFaceAuthenticated(mSelectedUserInteractor.getSelectedUserId(), - true /* isClass3Biometric */); - setKeyguardBouncerVisibility(true); - mTestableLooper.processAllMessages(); - - verifyFaceAuthenticateNeverCalled(); - } - - @Test public void testFaceAndFingerprintLockout_onlyFace() { mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); @@ -1379,7 +1062,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testGetUserCanSkipBouncer_whenFace() { int user = mSelectedUserInteractor.getSelectedUserId(); - mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isClass3Biometric */); + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(true /* isClass3Biometric */)) + .thenReturn(true); + when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(true); + when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true); + assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue(); } @@ -1388,7 +1075,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */)) .thenReturn(false); int user = mSelectedUserInteractor.getSelectedUserId(); - mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isClass3Biometric */); + when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false); + when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true); + assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse(); } @@ -1409,22 +1098,19 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testBiometricsCleared_whenUserSwitches() throws Exception { + public void testBiometricsCleared_whenUserSwitches() { final BiometricAuthenticated dummyAuthentication = new BiometricAuthenticated(true /* authenticated */, true /* strong */); - mKeyguardUpdateMonitor.mUserFaceAuthenticated.put(0 /* user */, dummyAuthentication); mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.put(0 /* user */, dummyAuthentication); assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1); - assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1); mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> { }); assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0); - assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0); } @Test - public void testMultiUserJankMonitor_whenUserSwitches() throws Exception { + public void testMultiUserJankMonitor_whenUserSwitches() { mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */); verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH); verify(mLatencyTracker).onActionEnd(LatencyTracker.ACTION_USER_SWITCH); @@ -1432,22 +1118,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testMultiUserLockoutChanged_whenUserSwitches() { - testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT, - BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT); + testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT); } @Test public void testMultiUserLockoutNotChanged_whenUserSwitches() { - testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_NONE, - BiometricConstants.BIOMETRIC_LOCKOUT_NONE); + testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_NONE); } private void testMultiUserLockout_whenUserSwitches( - @BiometricConstants.LockoutMode int fingerprintLockoutMode, - @BiometricConstants.LockoutMode int faceLockoutMode) { + @BiometricConstants.LockoutMode int fingerprintLockoutMode) { final int newUser = 12; - final boolean faceLockOut = - faceLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE; final boolean fpLockOut = fingerprintLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE; @@ -1455,16 +1136,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); keyguardIsVisible(); - verifyFaceAuthenticateCall(); verifyFingerprintAuthenticateCall(); when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser))) .thenReturn(fingerprintLockoutMode); - when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser))) - .thenReturn(faceLockoutMode); - final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal); - mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel; KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); mKeyguardUpdateMonitor.registerCallback(callback); @@ -1472,17 +1148,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.handleUserSwitchComplete(newUser); mTestableLooper.processAllMessages(); - // THEN face and fingerprint listening are always cancelled immediately - verify(faceCancel).cancel(); - verify(callback).onBiometricRunningStateChanged( - eq(false), eq(BiometricSourceType.FACE)); + // THEN fingerprint listening are always cancelled immediately verify(fpCancel).cancel(); verify(callback).onBiometricRunningStateChanged( eq(false), eq(BiometricSourceType.FINGERPRINT)); // THEN locked out states are updated assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLockOut); - assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLockOut); // Fingerprint should be cancelled on lockout if going to lockout state, else // restarted if it's not @@ -1752,11 +1424,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldNotListenForUdfps_whenFaceAuthenticated() { // GIVEN a "we should listen for udfps" state mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); + when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true); when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); // WHEN face authenticated - mKeyguardUpdateMonitor.onFaceAuthenticated( - mSelectedUserInteractor.getSelectedUserId(), false); + when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true); // THEN we shouldn't listen for udfps assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false); @@ -1777,51 +1449,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() { - // GIVEN state for face auth should run aside from StatusBarState - biometricsNotDisabledThroughDevicePolicyManager(); - mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED); - setKeyguardBouncerVisibility(false /* isVisible */); - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - lockscreenBypassIsAllowed(); - keyguardIsVisible(); - - // WHEN status bar state reports a change to the keyguard that would normally indicate to - // start running face auth - mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isEqualTo(true); - - // THEN face unlock is not running b/c status bar state changes don't cause biometric - // listening state to update - assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isEqualTo(false); - - // WHEN biometric listening state is updated when showing state changes from false => true - mKeyguardUpdateMonitor.setKeyguardShowing(false, false); - mKeyguardUpdateMonitor.setKeyguardShowing(true, false); - - // THEN face unlock is running - assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isEqualTo(true); - } - - @Test - public void testRequestFaceAuthFromOccludingApp_whenInvoked_startsFaceAuth() { - mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true); - - assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue(); - } - - @Test - public void testRequestFaceAuthFromOccludingApp_whenInvoked_stopsFaceAuth() { - mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true); - - assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue(); - - mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false); - - assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse(); - } - - @Test public void testRequireUnlockForNfc_Broadcast() { KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); mKeyguardUpdateMonitor.registerCallback(callback); @@ -1833,13 +1460,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testFaceDoesNotAuth_afterPinAttempt() { - mTestableLooper.processAllMessages(); - mKeyguardUpdateMonitor.setCredentialAttempted(); - verifyFaceAuthenticateNeverCalled(); - } - - @Test public void testShowTrustGrantedMessage_onTrustGranted() { // WHEN trust is enabled (ie: via some trust agent) with a trustGranted string mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, @@ -1855,366 +1475,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() { - cleanupKeyguardUpdateMonitor(); - mFaceManager = null; - - mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException { - // Face auth should run when the following is true. - keyguardNotGoingAway(); - occludingAppRequestsFaceAuth(); - currentUserIsSystem(); - primaryAuthNotRequiredByStrongAuthTracker(); - biometricsEnabledForCurrentUser(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - // Fingerprint is locked out. - fingerprintErrorTemporaryLockOut(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse() - throws RemoteException { - // Face auth should run when the following is true. - bouncerFullyVisibleAndNotGoingToSleep(); - keyguardNotGoingAway(); - currentUserIsSystem(); - primaryAuthNotRequiredByStrongAuthTracker(); - biometricsEnabledForCurrentUser(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - userNotCurrentlySwitching(); - - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - triggerSuccessfulFaceAuth(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenFpIsAlreadyAuthenticated_returnsFalse() - throws RemoteException { - // Face auth should run when the following is true. - bouncerFullyVisibleAndNotGoingToSleep(); - keyguardNotGoingAway(); - currentUserIsSystem(); - primaryAuthNotRequiredByStrongAuthTracker(); - biometricsEnabledForCurrentUser(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - userNotCurrentlySwitching(); - - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - successfulFingerprintAuth(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException { - cleanupKeyguardUpdateMonitor(); - // This disables face auth - when(mUserManager.isSystemUser()).thenReturn(false); - mKeyguardUpdateMonitor = - new TestableKeyguardUpdateMonitor(mContext); - - // Face auth should run when the following is true. - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - primaryAuthNotRequiredByStrongAuthTracker(); - biometricsEnabledForCurrentUser(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse() - throws RemoteException { - // Face auth should run when the following is true. - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - biometricsEnabledForCurrentUser(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - userNotCurrentlySwitching(); - - // This disables face auth - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse() - throws RemoteException { - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - // This disables face auth - biometricsDisabledForCurrentUser(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse() - throws RemoteException { - // Face auth should run when the following is true. - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - userCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse() - throws RemoteException { - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - secureCameraLaunched(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test - public void shouldListenForFace_secureCameraLaunchedButAlternateBouncerIsLaunched_returnsTrue() - throws RemoteException { - // Face auth should run when the following is true. - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - secureCameraLaunched(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - - alternateBouncerVisible(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test - public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue() - throws RemoteException { - // Face auth should run when the following is true. - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - - bouncerFullyVisibleAndNotGoingToSleep(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test - public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue() - throws RemoteException { - // Face auth should run when the following is true. - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - - triggerAuthInterrupt(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test - public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException { - // Preconditions for face auth to run - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - - statusBarShadeIsLocked(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - - deviceNotGoingToSleep(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - deviceIsInteractive(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - keyguardIsVisible(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - statusBarShadeIsNotLocked(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test - public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException { - // Preconditions for face auth to run - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - when(mAuthController.isUdfpsFingerDown()).thenReturn(false); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - - when(mAuthController.isUdfpsFingerDown()).thenReturn(true); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test - public void testShouldListenForFace_whenAlternateBouncerIsShowing_returnsTrue() - throws RemoteException { - // Preconditions for face auth to run - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mTestableLooper.processAllMessages(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - - mKeyguardUpdateMonitor.setAlternateBouncerShowing(true); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test - public void testShouldListenForFace_alternateBouncerShowingButDeviceGoingToSleep_returnsFalse() - throws RemoteException { - // Preconditions for face auth to run - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - deviceNotGoingToSleep(); - alternateBouncerVisible(); - mTestableLooper.processAllMessages(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - deviceGoingToSleep(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - private void alternateBouncerVisible() { - mKeyguardUpdateMonitor.setAlternateBouncerShowing(true); - } - - @Test - public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue() - throws RemoteException { - // Preconditions for face auth to run - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - mKeyguardUpdateMonitor.setAlternateBouncerShowing(true); - mTestableLooper.processAllMessages(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - // Face is locked out. - faceAuthLockOut(); - mTestableLooper.processAllMessages(); - - // This is needed beccause we want to show face locked out error message whenever face auth - // is supposed to run. - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() { mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); keyguardIsVisible(); - verifyFaceAuthenticateCall(); verifyFingerprintAuthenticateCall(); - mKeyguardUpdateMonitor.onFaceAuthenticated(0, false); + when(mFaceAuthInteractor.isAuthenticated()).thenReturn(true); + when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(false); + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */)) + .thenReturn(false); // Make sure keyguard is going away after face auth attempt, and that it calls // updateBiometricStateListeningState. mKeyguardUpdateMonitor.setKeyguardShowing(false, false); @@ -2234,34 +1505,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testDreamingStopped_faceDoesNotRun() { - mKeyguardUpdateMonitor.dispatchDreamingStopped(); - mTestableLooper.processAllMessages(); - - verifyFaceAuthenticateNeverCalled(); - } - - @Test - public void testFaceWakeupTrigger_runFaceAuth_onlyOnConfiguredTriggers() { - // keyguard is visible - keyguardIsVisible(); - - // WHEN device wakes up from an application - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_APPLICATION); - mTestableLooper.processAllMessages(); - - // THEN face auth isn't triggered - verifyFaceAuthenticateNeverCalled(); - - // WHEN device wakes up from the power button - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - // THEN face auth is triggered - verifyFaceAuthenticateCall(); - } - - @Test public void testOnTrustGrantedForCurrentUser_dismissKeyguardRequested_deviceInteractive() { // GIVEN device is interactive deviceIsInteractive(); @@ -2422,18 +1665,15 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testStrongAuthChange_lockDown_stopsFpAndFaceListeningState() { - // GIVEN device is listening for face and fingerprint + public void testStrongAuthChange_lockDown_stopsFpListeningState() { + // GIVEN device is listening for fingerprint mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); keyguardIsVisible(); - verifyFaceAuthenticateCall(); verifyFingerprintAuthenticateCall(); - final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal); - mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel; KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); mKeyguardUpdateMonitor.registerCallback(callback); @@ -2445,88 +1685,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mSelectedUserInteractor.getSelectedUserId()); mTestableLooper.processAllMessages(); - // THEN face and fingerprint listening are cancelled - verify(faceCancel).cancel(); - verify(callback).onBiometricRunningStateChanged( - eq(false), eq(BiometricSourceType.FACE)); + // THEN fingerprint listening are cancelled verify(fpCancel).cancel(); verify(callback).onBiometricRunningStateChanged( eq(false), eq(BiometricSourceType.FINGERPRINT)); } @Test - public void testNonStrongBiometricAllowedChanged_stopsFaceListeningState() { - // GIVEN device is listening for face and fingerprint - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - keyguardIsVisible(); - - verifyFaceAuthenticateCall(); - - final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); - mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; - KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); - mKeyguardUpdateMonitor.registerCallback(callback); - - // WHEN non-strong biometric allowed changes - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); - mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged( - mSelectedUserInteractor.getSelectedUserId()); - mTestableLooper.processAllMessages(); - - // THEN face and fingerprint listening are cancelled - verify(faceCancel).cancel(); - verify(callback).onBiometricRunningStateChanged( - eq(false), eq(BiometricSourceType.FACE)); - } - - @Test - public void testPostureChangeToUnsupported_stopsFaceListeningState() { - // GIVEN device is listening for face - mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED; - deviceInPostureStateClosed(); - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - keyguardIsVisible(); - - verifyFaceAuthenticateCall(); - - final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); - mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; - KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); - mKeyguardUpdateMonitor.registerCallback(callback); - - // WHEN device is opened - deviceInPostureStateOpened(); - mTestableLooper.processAllMessages(); - - // THEN face listening is stopped. - verify(faceCancel).cancel(); - verify(callback).onBiometricRunningStateChanged( - eq(false), eq(BiometricSourceType.FACE)); - } - - @Test - public void testShouldListenForFace_withLockedDown_returnsFalse() - throws RemoteException { - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - supportsFaceDetection(); - mTestableLooper.processAllMessages(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - userDeviceLockDown(); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - } - - @Test public void assistantVisible_requestActiveUnlock() { // GIVEN active unlock requests from the assistant are allowed when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin( @@ -2549,8 +1714,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void fingerprintFailure_requestActiveUnlock_dismissKeyguard() - throws RemoteException { + public void fingerprintFailure_requestActiveUnlock_dismissKeyguard() { // GIVEN shouldTriggerActiveUnlock bouncerFullyVisible(); when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn( @@ -2571,8 +1735,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void faceNonBypassFailure_requestActiveUnlock_doesNotDismissKeyguard() - throws RemoteException { + public void faceNonBypassFailure_requestActiveUnlock_doesNotDismissKeyguard() { // GIVEN shouldTriggerActiveUnlock when(mAuthController.isUdfpsFingerDown()).thenReturn(false); keyguardIsVisible(); @@ -2588,7 +1751,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN face fails & bypass is not allowed lockscreenBypassIsNotAllowed(); - mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed(); + mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged( + new FailedFaceAuthenticationStatus()); // THEN request unlock with NO keyguard dismissal verify(mTrustManager).reportUserRequestedUnlock( @@ -2597,10 +1761,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void faceBypassFailure_requestActiveUnlock_dismissKeyguard() - throws RemoteException { + public void faceBypassFailure_requestActiveUnlock_dismissKeyguard() { // GIVEN shouldTriggerActiveUnlock when(mAuthController.isUdfpsFingerDown()).thenReturn(false); + when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true); keyguardIsVisible(); keyguardNotGoingAway(); statusBarShadeIsNotLocked(); @@ -2614,7 +1778,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN face fails & bypass is not allowed lockscreenBypassIsAllowed(); - mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed(); + mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged( + new FailedFaceAuthenticationStatus()); // THEN request unlock with a keyguard dismissal verify(mTrustManager).reportUserRequestedUnlock( @@ -2623,10 +1788,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void faceNonBypassFailure_requestActiveUnlock_dismissKeyguard() - throws RemoteException { + public void faceNonBypassFailure_requestActiveUnlock_dismissKeyguard() { // GIVEN shouldTriggerActiveUnlock when(mAuthController.isUdfpsFingerDown()).thenReturn(false); + when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true); lockscreenBypassIsNotAllowed(); when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn( true); @@ -2638,7 +1803,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN face fails & on the bouncer bouncerFullyVisible(); - mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed(); + mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged( + new FailedFaceAuthenticationStatus()); // THEN request unlock with a keyguard dismissal verify(mTrustManager).reportUserRequestedUnlock( @@ -2647,54 +1813,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testShouldListenForFace_withAuthSupportPostureConfig_returnsTrue() - throws RemoteException { - mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED; - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - supportsFaceDetection(); - - deviceInPostureStateOpened(); - mTestableLooper.processAllMessages(); - // Should not listen for face when posture state in DEVICE_POSTURE_OPENED - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - - deviceInPostureStateClosed(); - mTestableLooper.processAllMessages(); - // Should listen for face when posture state in DEVICE_POSTURE_CLOSED - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test - public void testShouldListenForFace_withoutAuthSupportPostureConfig_returnsTrue() - throws RemoteException { - mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_UNKNOWN; - keyguardNotGoingAway(); - bouncerFullyVisibleAndNotGoingToSleep(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - supportsFaceDetection(); - - deviceInPostureStateClosed(); - mTestableLooper.processAllMessages(); - // Whether device in any posture state, always listen for face - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - deviceInPostureStateOpened(); - mTestableLooper.processAllMessages(); - // Whether device in any posture state, always listen for face - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - } - - @Test public void testBatteryChangedIntent_refreshBatteryInfo() { mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(mContext, getBatteryIntent()); @@ -2727,8 +1845,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard() - throws RemoteException { + public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard() { // GIVEN shouldTriggerActiveUnlock keyguardIsVisible(); when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn( @@ -2754,8 +1871,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void unfoldWakeup_requestActiveUnlock_noDismissKeyguard() - throws RemoteException { + public void unfoldWakeup_requestActiveUnlock_noDismissKeyguard() { // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE keyguardIsVisible(); when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn( @@ -2781,8 +1897,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void unfoldFromPostureChange_requestActiveUnlock_forceDismissKeyguard() - throws RemoteException { + public void unfoldFromPostureChange_requestActiveUnlock_forceDismissKeyguard() { // GIVEN shouldTriggerActiveUnlock keyguardIsVisible(); when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn( @@ -2809,8 +1924,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test - public void unfoldFromPostureChange_requestActiveUnlock_noDismissKeyguard() - throws RemoteException { + public void unfoldFromPostureChange_requestActiveUnlock_noDismissKeyguard() { // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE keyguardIsVisible(); when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn( @@ -2859,44 +1973,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void faceAuthenticateOptions_bouncerAuthenticateReason() { - // GIVEN the bouncer is fully visible - bouncerFullyVisible(); - - // WHEN authenticate is called - ArgumentCaptor<FaceAuthenticateOptions> captor = - ArgumentCaptor.forClass(FaceAuthenticateOptions.class); - verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture()); - - // THEN the authenticate reason is attributed to the bouncer - assertThat(captor.getValue().getAuthenticateReason()) - .isEqualTo(AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN); - } - - @Test - public void faceAuthenticateOptions_wakingUpAuthenticateReason_powerButtonWakeReason() { - // GIVEN keyguard is visible - keyguardIsVisible(); - - // WHEN device wakes up from the power button - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - // THEN face auth is triggered - ArgumentCaptor<FaceAuthenticateOptions> captor = - ArgumentCaptor.forClass(FaceAuthenticateOptions.class); - verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture()); - - // THEN the authenticate reason is attributed to the waking - assertThat(captor.getValue().getAuthenticateReason()) - .isEqualTo(AUTHENTICATE_REASON_STARTED_WAKING_UP); - - // THEN the wake reason is attributed to the power button - assertThat(captor.getValue().getWakeReason()) - .isEqualTo(PowerManager.WAKE_REASON_POWER_BUTTON); - } - - @Test public void testFingerprintSensorProperties() throws RemoteException { mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered( new ArrayList<>()); @@ -2913,26 +1989,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testFaceSensorProperties() throws RemoteException { - // GIVEN no face sensor properties - when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true); - mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>()); - - // THEN face is not possible - assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible( - mSelectedUserInteractor.getSelectedUserId())).isFalse(); - - // WHEN there are face sensor properties - mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties); - - // THEN face is possible but face does NOT start listening immediately - assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible( - mSelectedUserInteractor.getSelectedUserId())).isTrue(); - verifyFaceAuthenticateNeverCalled(); - verifyFaceDetectNeverCalled(); - } - - @Test public void testFingerprintListeningStateWhenOccluded() { when(mAuthController.isUdfpsSupported()).thenReturn(true); @@ -3014,123 +2070,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { authCallback.getValue().onEnrollmentsChanged(BiometricAuthenticator.TYPE_FACE); mTestableLooper.processAllMessages(); verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE); - } - - @Test - public void onDisplayOn_nothingHappens() throws RemoteException { - // GIVEN - keyguardIsVisible(); - enableStopFaceAuthOnDisplayOff(); - - // WHEN the default display state changes to ON - triggerDefaultDisplayStateChangeToOn(); - - // THEN face auth is NOT started since we rely on STARTED_WAKING_UP to start face auth, - // NOT the display on event - verifyFaceAuthenticateNeverCalled(); - verifyFaceDetectNeverCalled(); - } - - @Test - public void onDisplayOff_stopFaceAuth() throws RemoteException { - enableStopFaceAuthOnDisplayOff(); - - // GIVEN device is listening for face - mKeyguardUpdateMonitor.setKeyguardShowing(true, false); - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - verifyFaceAuthenticateCall(); - - final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); - mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; - KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); - mKeyguardUpdateMonitor.registerCallback(callback); - - // WHEN the default display state changes to OFF - triggerDefaultDisplayStateChangeToOff(); - - // THEN face listening is stopped. - verify(faceCancel).cancel(); - verify(callback).onBiometricRunningStateChanged( - eq(false), eq(BiometricSourceType.FACE)); - } - - @Test - public void onDisplayOff_whileAsleep_doesNotStopFaceAuth() throws RemoteException { - enableStopFaceAuthOnDisplayOff(); - when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_ASLEEP); - - // GIVEN device is listening for face - mKeyguardUpdateMonitor.setKeyguardShowing(true, false); - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - verifyFaceAuthenticateCall(); - - final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); - mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; - KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); - mKeyguardUpdateMonitor.registerCallback(callback); - // WHEN the default display state changes to OFF - triggerDefaultDisplayStateChangeToOff(); - - // THEN face listening is NOT stopped. - verify(faceCancel, never()).cancel(); - verify(callback, never()).onBiometricRunningStateChanged( - eq(false), eq(BiometricSourceType.FACE)); - } - - @Test - public void onDisplayOff_whileWaking_doesNotStopFaceAuth() throws RemoteException { - enableStopFaceAuthOnDisplayOff(); - when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_WAKING); - - // GIVEN device is listening for face - mKeyguardUpdateMonitor.setKeyguardShowing(true, false); - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + clearInvocations(callback); + mFaceAuthenticationListener.getValue().onAuthEnrollmentStateChanged(false); mTestableLooper.processAllMessages(); - verifyFaceAuthenticateCall(); - - final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); - mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; - KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); - mKeyguardUpdateMonitor.registerCallback(callback); - - // WHEN the default display state changes to OFF - triggerDefaultDisplayStateChangeToOff(); - - // THEN face listening is NOT stopped. - verify(faceCancel, never()).cancel(); - verify(callback, never()).onBiometricRunningStateChanged( - eq(false), eq(BiometricSourceType.FACE)); - } - - private void triggerDefaultDisplayStateChangeToOn() { - triggerDefaultDisplayStateChangeTo(true); - } - - private void triggerDefaultDisplayStateChangeToOff() { - triggerDefaultDisplayStateChangeTo(false); - } - - /** - * @param on true for Display.STATE_ON, else Display.STATE_OFF - */ - private void triggerDefaultDisplayStateChangeTo(boolean on) { - DisplayManagerGlobal displayManagerGlobal = mock(DisplayManagerGlobal.class); - DisplayInfo displayInfoWithDisplayState = new DisplayInfo(); - displayInfoWithDisplayState.state = on ? Display.STATE_ON : Display.STATE_OFF; - when(displayManagerGlobal.getDisplayInfo(mDisplayTracker.getDefaultDisplayId())) - .thenReturn(displayInfoWithDisplayState); - mDisplayTracker.setAllDisplays(new Display[]{ - new Display( - displayManagerGlobal, - mDisplayTracker.getDefaultDisplayId(), - displayInfoWithDisplayState, - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS - ) - }); - mDisplayTracker.triggerOnDisplayChanged(mDisplayTracker.getDefaultDisplayId()); + verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE); } private void verifyFingerprintAuthenticateNeverCalled() { @@ -3151,37 +2095,12 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verify(mFingerprintManager).detectFingerprint(any(), any(), any()); } - private void verifyFaceAuthenticateNeverCalled() { - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), any()); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); - } - - private void verifyFaceAuthenticateCall() { - verify(mFaceManager).authenticate(any(), any(), any(), any(), any()); - } - - private void verifyFaceDetectNeverCalled() { - verify(mFaceManager, never()).detectFace(any(), any(), any()); - } - - private void verifyFaceDetectCall() { - verify(mFaceManager).detectFace(any(), any(), any()); - } - private void userDeviceLockDown() { when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId)) .thenReturn(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); } - private void supportsFaceDetection() throws RemoteException { - final boolean isClass3 = !mFaceSensorProperties.isEmpty() - && mFaceSensorProperties.get(0).sensorStrength == STRENGTH_STRONG; - mFaceSensorProperties = - List.of(createFaceSensorProperties(/* supportsFaceDetection = */ true, isClass3)); - mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties); - } - private void lockscreenBypassIsAllowed() { mockCanBypassLockscreen(true); } @@ -3204,38 +2123,20 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void faceAuthLockOut() { - mKeyguardUpdateMonitor.mFaceAuthenticationCallback - .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, ""); + when(mFaceAuthInteractor.isLockedOut()).thenReturn(true); + mFaceAuthenticationListener.getValue().onAuthenticationStatusChanged( + new ErrorFaceAuthenticationStatus(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "", + 0L)); } private void statusBarShadeIsNotLocked() { mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); } - private void statusBarShadeIsLocked() { - mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED); - } - private void keyguardIsVisible() { mKeyguardUpdateMonitor.setKeyguardShowing(true, false); } - private void triggerAuthInterrupt() { - mKeyguardUpdateMonitor.onAuthInterruptDetected(true); - } - - private void occludingAppRequestsFaceAuth() { - mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true); - } - - private void secureCameraLaunched() { - mKeyguardUpdateMonitor.onCameraLaunched(); - } - - private void userCurrentlySwitching() { - mKeyguardUpdateMonitor.setSwitchingUser(true); - } - private void fingerprintErrorTemporaryLockOut() { mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out"); @@ -3245,100 +2146,25 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED); } - private void deviceInPostureStateClosed() { - mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_CLOSED); - } - - private void successfulFingerprintAuth() { - mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback - .onAuthenticationSucceeded( - new FingerprintManager.AuthenticationResult(null, - null, - mCurrentUserId, - true)); - } - - private void triggerSuccessfulFaceAuth() { - mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); - verify(mFaceManager).authenticate(any(), - any(), - mAuthenticationCallbackCaptor.capture(), - any(), - any()); - mAuthenticationCallbackCaptor.getValue() - .onAuthenticationSucceeded( - new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false)); - } - private void currentUserIsSystem() { when(mUserManager.isSystemUser()).thenReturn(true); } - private void biometricsNotDisabledThroughDevicePolicyManager() { - when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, - mSelectedUserInteractor.getSelectedUserId())).thenReturn(0); - } - private void biometricsEnabledForCurrentUser() throws RemoteException { mBiometricEnabledOnKeyguardCallback.onChanged(true, mSelectedUserInteractor.getSelectedUserId()); } - private void biometricsDisabledForCurrentUser() throws RemoteException { - mBiometricEnabledOnKeyguardCallback.onChanged( - false, - mSelectedUserInteractor.getSelectedUserId() - ); - } - - private void primaryAuthRequiredEncrypted() { - when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId())) - .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT); - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); - } - - private void primaryAuthRequiredForWeakBiometricOnly() { - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(true))).thenReturn(true); - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(false))).thenReturn(false); - } - private void primaryAuthNotRequiredByStrongAuthTracker() { when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId())) .thenReturn(0); when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); } - private void currentUserDoesNotHaveTrust() { - mKeyguardUpdateMonitor.onTrustChanged( - false, - false, - mSelectedUserInteractor.getSelectedUserId(), - -1, - new ArrayList<>() - ); - } - - private void userNotCurrentlySwitching() { - mKeyguardUpdateMonitor.setSwitchingUser(false); - } - private void keyguardNotGoingAway() { mKeyguardUpdateMonitor.setKeyguardGoingAway(false); } - private void bouncerFullyVisibleAndNotGoingToSleep() { - bouncerFullyVisible(); - deviceNotGoingToSleep(); - } - - private void deviceNotGoingToSleep() { - mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1); - } - - private void deviceGoingToSleep() { - mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(/* value doesn't matter */1); - } - private void deviceIsInteractive() { mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); } @@ -3347,20 +2173,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { setKeyguardBouncerVisibility(true); } - private void bouncerNotVisible() { - setKeyguardBouncerVisibility(false); - } - private void setKeyguardBouncerVisibility(boolean isVisible) { mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible); mTestableLooper.processAllMessages(); } - private void givenUdfpsSupported() { - when(mAuthController.isUdfpsSupported()).thenReturn(true); - Assert.assertTrue(mKeyguardUpdateMonitor.isUdfpsSupported()); - } - private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) { BroadcastReceiver.PendingResult pendingResult = new BroadcastReceiver.PendingResult(Activity.RESULT_OK, @@ -3386,31 +2203,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } - private void givenDetectFace() throws RemoteException { - // GIVEN bypass is enabled, face detection is supported and primary auth is required - lockscreenBypassIsAllowed(); - supportsFaceDetection(); - primaryAuthRequiredEncrypted(); - keyguardIsVisible(); - // fingerprint is NOT running, UDFPS is NOT supported - - // WHEN the device wakes up - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - } - - private void enableStopFaceAuthOnDisplayOff() throws RemoteException { - cleanupKeyguardUpdateMonitor(); - clearInvocations(mFaceManager); - clearInvocations(mFingerprintManager); - clearInvocations(mBiometricManager); - clearInvocations(mStatusBarStateController); - mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext); - setupBiometrics(mKeyguardUpdateMonitor); - when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); - assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(1); - } - private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) { int subscription = simInited ? 1/* mock subid=1 */ : SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE; @@ -3486,11 +2278,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitorLogger, mUiEventLogger, () -> mSessionTracker, mTrustManager, mSubscriptionManager, mUserManager, mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager, - mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager, + mPackageManager, mFingerprintManager, mBiometricManager, mFaceWakeUpTriggersConfig, mDevicePostureController, Optional.of(mInteractiveToAuthProvider), - mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker, - mWakefulness, mSelectedUserInteractor); + mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index d2f45ae8685a..3d7d701ee5d6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -18,7 +18,6 @@ package com.android.keyguard; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; -import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; @@ -151,7 +150,6 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR); mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(FACE_AUTH_REFACTOR, false); mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt index ea7cc3dcd119..6c91c987f327 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt @@ -100,7 +100,7 @@ class FaceScanningProviderFactoryTest : SysuiTestCase() { @Test fun shouldNotShowFaceScanningAnimationIfFaceIsNotEnrolled() { - whenever(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false) + whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false) whenever(authController.isShowing).thenReturn(true) assertThat(underTest.shouldShowFaceScanningAnim()).isFalse() @@ -108,7 +108,7 @@ class FaceScanningProviderFactoryTest : SysuiTestCase() { @Test fun shouldShowFaceScanningAnimationIfBiometricPromptIsShowing() { - whenever(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) + whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true) whenever(authController.isShowing).thenReturn(true) assertThat(underTest.shouldShowFaceScanningAnim()).isTrue() @@ -116,7 +116,7 @@ class FaceScanningProviderFactoryTest : SysuiTestCase() { @Test fun shouldShowFaceScanningAnimationIfKeyguardFaceDetectionIsShowing() { - whenever(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) + whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true) whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(true) assertThat(underTest.shouldShowFaceScanningAnim()).isTrue() 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 11c5d3bb27b3..602f3dc29491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -475,6 +475,22 @@ public class AuthControllerTest extends SysuiTestCase { } @Test + public void testOnAuthenticationFailedInvoked_whenBiometricReEnrollRequired() { + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + final int modality = BiometricAuthenticator.TYPE_FACE; + mAuthController.onBiometricError(modality, + BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL, + 0 /* vendorCode */); + + verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(), + mMessageCaptor.capture()); + + assertThat(mModalityCaptor.getValue()).isEqualTo(modality); + assertThat(mMessageCaptor.getValue()).isEqualTo(mContext.getString( + R.string.face_recalibrate_notification_content)); + } + + @Test public void testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withPaused() { testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected( BiometricConstants.BIOMETRIC_PAUSED_REJECTED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt index 00ea78f01fa9..993dbac747e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt @@ -65,10 +65,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, false) - set(Flags.FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, ) private val detector: AuthDialogPanelInteractionDetector = testComponent.underTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt index ec17794d4ee2..86b9b84ab36e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt @@ -21,13 +21,10 @@ import android.view.View import android.view.accessibility.AccessibilityNodeInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.keyguard.FaceAuthApiRequestReason -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import org.junit.Assert.assertEquals import org.junit.Before @@ -44,7 +41,6 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper class FaceAuthAccessibilityDelegateTest : SysuiTestCase() { - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var hostView: View @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor private lateinit var underTest: FaceAuthAccessibilityDelegate @@ -55,14 +51,13 @@ class FaceAuthAccessibilityDelegateTest : SysuiTestCase() { underTest = FaceAuthAccessibilityDelegate( context.resources, - keyguardUpdateMonitor, faceAuthInteractor, ) } @Test fun shouldListenForFaceTrue_onInitializeAccessibilityNodeInfo_clickActionAdded() { - whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true) + whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true) // WHEN node is initialized val mockedNodeInfo = mock(AccessibilityNodeInfo::class.java) @@ -81,7 +76,7 @@ class FaceAuthAccessibilityDelegateTest : SysuiTestCase() { @Test fun shouldListenForFaceFalse_onInitializeAccessibilityNodeInfo_clickActionNotAdded() { - whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(false) + whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(false) // WHEN node is initialized val mockedNodeInfo = mock(AccessibilityNodeInfo::class.java) @@ -94,7 +89,7 @@ class FaceAuthAccessibilityDelegateTest : SysuiTestCase() { @Test fun performAccessibilityAction_actionClick_retriesFaceAuth() { - whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true) + whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true) // WHEN click action is performed underTest.performAccessibilityAction( @@ -103,9 +98,6 @@ class FaceAuthAccessibilityDelegateTest : SysuiTestCase() { null ) - // THEN retry face auth - verify(keyguardUpdateMonitor) - .requestFaceAuth(eq(FaceAuthApiRequestReason.ACCESSIBILITY_ACTION)) verify(faceAuthInteractor).onAccessibilityAction() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 5f0d4d428322..f5b6f14a627c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -36,13 +36,13 @@ import android.view.accessibility.AccessibilityManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R @@ -107,7 +107,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsView: UdfpsView @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator - @Mock private lateinit var featureFlags: FeatureFlags @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor @@ -123,47 +122,52 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Before fun setup() { whenever(inflater.inflate(R.layout.udfps_view, null, false)) - .thenReturn(udfpsView) + .thenReturn(udfpsView) whenever(inflater.inflate(R.layout.udfps_bp_view, null)) - .thenReturn(mock(UdfpsBpView::class.java)) + .thenReturn(mock(UdfpsBpView::class.java)) whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) - .thenReturn(mUdfpsKeyguardViewLegacy) + .thenReturn(mUdfpsKeyguardViewLegacy) whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null)) - .thenReturn(mock(UdfpsFpmEmptyView::class.java)) + .thenReturn(mock(UdfpsFpmEmptyView::class.java)) } private fun withReason( - @ShowReason reason: Int, - isDebuggable: Boolean = false, - block: () -> Unit + @ShowReason reason: Int, + isDebuggable: Boolean = false, + enableDeviceEntryUdfpsRefactor: Boolean = false, + block: () -> Unit, ) { + if (enableDeviceEntryUdfpsRefactor) { + mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) + } else { + mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) + } controllerOverlay = UdfpsControllerOverlay( - context, - inflater, - windowManager, - accessibilityManager, - statusBarStateController, - statusBarKeyguardViewManager, - keyguardUpdateMonitor, - dialogManager, - dumpManager, - transitionController, - configurationController, - keyguardStateController, - unlockedScreenOffAnimationController, - udfpsDisplayMode, - REQUEST_ID, - reason, - controllerCallback, - onTouch, - activityLaunchAnimator, - featureFlags, - primaryBouncerInteractor, - alternateBouncerInteractor, - isDebuggable, - udfpsKeyguardAccessibilityDelegate, - keyguardTransitionInteractor, - mSelectedUserInteractor, + context, + inflater, + windowManager, + accessibilityManager, + statusBarStateController, + statusBarKeyguardViewManager, + keyguardUpdateMonitor, + dialogManager, + dumpManager, + transitionController, + configurationController, + keyguardStateController, + unlockedScreenOffAnimationController, + udfpsDisplayMode, + REQUEST_ID, + reason, + controllerCallback, + onTouch, + activityLaunchAnimator, + primaryBouncerInteractor, + alternateBouncerInteractor, + isDebuggable, + udfpsKeyguardAccessibilityDelegate, + keyguardTransitionInteractor, + mSelectedUserInteractor, ) block() } @@ -185,12 +189,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT) val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT) overlayParams = UdfpsOverlayParams( - sensorBounds, - overlayBounds, - DISPLAY_WIDTH, - DISPLAY_HEIGHT, - scaleFactor = 1f, - rotation + sensorBounds, + overlayBounds, + DISPLAY_WIDTH, + DISPLAY_HEIGHT, + scaleFactor = 1f, + rotation ) block() } @@ -200,8 +204,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { withReason(REASON_AUTH_BP) { controllerOverlay.show(udfpsController, overlayParams) verify(windowManager).addView( - eq(controllerOverlay.overlayView), - layoutParamsCaptor.capture() + eq(controllerOverlay.getTouchOverlay()), + layoutParamsCaptor.capture() ) // ROTATION_0 is the native orientation. Sensor should stay in the top left corner. @@ -218,8 +222,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { withReason(REASON_AUTH_BP) { controllerOverlay.show(udfpsController, overlayParams) verify(windowManager).addView( - eq(controllerOverlay.overlayView), - layoutParamsCaptor.capture() + eq(controllerOverlay.getTouchOverlay()), + layoutParamsCaptor.capture() ) // ROTATION_180 is not supported. Sensor should stay in the top left corner. @@ -236,8 +240,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { withReason(REASON_AUTH_BP) { controllerOverlay.show(udfpsController, overlayParams) verify(windowManager).addView( - eq(controllerOverlay.overlayView), - layoutParamsCaptor.capture() + eq(controllerOverlay.getTouchOverlay()), + layoutParamsCaptor.capture() ) // Sensor should be in the bottom left corner in ROTATION_90. @@ -254,8 +258,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { withReason(REASON_AUTH_BP) { controllerOverlay.show(udfpsController, overlayParams) verify(windowManager).addView( - eq(controllerOverlay.overlayView), - layoutParamsCaptor.capture() + eq(controllerOverlay.getTouchOverlay()), + layoutParamsCaptor.capture() ) // Sensor should be in the top right corner in ROTATION_270. @@ -270,7 +274,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { private fun showUdfpsOverlay() { val didShow = controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager).addView(eq(controllerOverlay.overlayView), any()) + verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any()) verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode)) verify(udfpsView).animationViewController = any() verify(udfpsView).addView(any()) @@ -278,7 +282,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { assertThat(didShow).isTrue() assertThat(controllerOverlay.isShowing).isTrue() assertThat(controllerOverlay.isHiding).isFalse() - assertThat(controllerOverlay.overlayView).isNotNull() + assertThat(controllerOverlay.getTouchOverlay()).isNotNull() } @Test @@ -295,14 +299,14 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { private fun hideUdfpsOverlay() { val didShow = controllerOverlay.show(udfpsController, overlayParams) - val view = controllerOverlay.overlayView + val view = controllerOverlay.getTouchOverlay() val didHide = controllerOverlay.hide() verify(windowManager).removeView(eq(view)) assertThat(didShow).isTrue() assertThat(didHide).isTrue() - assertThat(controllerOverlay.overlayView).isNull() + assertThat(controllerOverlay.getTouchOverlay()).isNull() assertThat(controllerOverlay.animationViewController).isNull() assertThat(controllerOverlay.isShowing).isFalse() assertThat(controllerOverlay.isHiding).isTrue() @@ -348,8 +352,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { controllerOverlay.show(udfpsController, overlayParams) verify(windowManager).addView( - eq(controllerOverlay.overlayView), - layoutParamsCaptor.capture() + eq(controllerOverlay.getTouchOverlay()), + layoutParamsCaptor.capture() ) // Layout params should use sensor bounds diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index c8c400de5740..e2cab29c473c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -73,6 +73,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; @@ -286,6 +287,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // Create a fake background executor. mBiometricExecutor = new FakeExecutor(new FakeSystemClock()); + mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); initUdfpsController(mOpticalProps); } @@ -304,7 +306,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mStatusBarKeyguardViewManager, mDumpManager, mKeyguardUpdateMonitor, - mFeatureFlags, mFalsingManager, mPowerManager, mAccessibilityManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index 9bff88bbf2dd..79f062536404 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState @@ -106,6 +107,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, + mock(KeyguardFaceAuthInteractor::class.java), ) mAlternateBouncerInteractor = AlternateBouncerInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt index b48bc1d28f86..094616f0682c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.shared.model.AuthenticationFlags import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown import com.android.systemui.res.R.string.kg_trust_agent_disabled @@ -62,6 +63,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -122,6 +124,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { fakeTrustRepository, testScope.backgroundScope, mSelectedUserInteractor, + mock(KeyguardFaceAuthInteractor::class.java), ) underTest = BouncerMessageInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt index d6aa9ac87d39..37a093e4c23f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.bouncer.domain.interactor -import android.hardware.biometrics.BiometricSourceType import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.testing.TestableResources @@ -35,6 +34,7 @@ import com.android.systemui.bouncer.ui.BouncerViewDelegate import com.android.systemui.classifier.FalsingCollector import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.statusbar.policy.KeyguardStateController @@ -72,6 +72,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor private lateinit var mainHandler: FakeHandler private lateinit var underTest: PrimaryBouncerInteractor private lateinit var resources: TestableResources @@ -103,6 +104,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { trustRepository, testScope.backgroundScope, mSelectedUserInteractor, + faceAuthInteractor, ) whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) whenever(repository.primaryBouncerShow.value).thenReturn(false) @@ -423,10 +425,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { mainHandler.setMode(FakeHandler.Mode.QUEUEING) // GIVEN bouncer should be delayed due to face auth - whenever(keyguardStateController.isFaceEnrolled).thenReturn(true) - whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) - .thenReturn(true) - whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(true) + whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true) // WHEN bouncer show is requested underTest.show(true) @@ -444,15 +443,12 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { } @Test - fun noDelayBouncer_biometricsAllowed_postureDoesNotAllowFaceAuth() { + fun noDelayBouncer_faceAuthNotAllowed() { mainHandler.setMode(FakeHandler.Mode.QUEUEING) // GIVEN bouncer should not be delayed because device isn't in the right posture for // face auth - whenever(keyguardStateController.isFaceEnrolled).thenReturn(true) - whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) - .thenReturn(true) - whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(false) + whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(false) // WHEN bouncer show is requested underTest.show(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt index d1b120e0948b..bdf5041f8a38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.TrustRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.utils.os.FakeHandler @@ -53,6 +54,7 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor private val mainHandler = FakeHandler(Looper.getMainLooper()) private lateinit var underTest: PrimaryBouncerInteractor @@ -75,6 +77,7 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { Mockito.mock(TrustRepository::class.java), TestScope().backgroundScope, mSelectedUserInteractor, + faceAuthInteractor, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt index 2cc8f0a47955..90e0c19b7c65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.TrustRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.utils.os.FakeHandler @@ -61,6 +62,7 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor lateinit var bouncerInteractor: PrimaryBouncerInteractor private val mainHandler = FakeHandler(Looper.getMainLooper()) @@ -86,6 +88,7 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { Mockito.mock(TrustRepository::class.java), TestScope().backgroundScope, mSelectedUserInteractor, + faceAuthInteractor, ) underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index 6c990e457209..40c9432d543d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -30,7 +30,6 @@ import android.testing.TestableLooper import android.view.SurfaceControlViewHost import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils -import com.android.systemui.res.R import com.android.systemui.SystemUIAppComponentFactoryBase import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator @@ -51,6 +50,7 @@ import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract @@ -173,7 +173,6 @@ class CustomizationProviderTest : SysuiTestCase() { FakeFeatureFlags().apply { set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true) set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true) - set(Flags.FACE_AUTH_REFACTOR, true) } underTest.interactor = KeyguardQuickAffordanceInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt index f0ff77ebf1ea..852f9a540221 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt @@ -52,7 +52,6 @@ class ResourceTrimmerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true) featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true) - featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) powerInteractor = PowerInteractorFactory.create().powerInteractor keyguardRepository.setDozeAmount(0f) keyguardRepository.setKeyguardGoingAway(false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 0b148d14a43f..45aca175657e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -58,7 +58,6 @@ import com.android.systemui.display.data.repository.display import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory @@ -188,11 +187,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { biometricSettingsRepository = FakeBiometricSettingsRepository() deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository() trustRepository = FakeTrustRepository() - featureFlags = - FakeFeatureFlags().apply { - set(FACE_AUTH_REFACTOR, true) - set(KEYGUARD_WM_STATE_REFACTOR, false) - } + featureFlags = FakeFeatureFlags().apply { set(KEYGUARD_WM_STATE_REFACTOR, false) } powerRepository = FakePowerRepository() powerInteractor = @@ -790,21 +785,19 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun everythingWorksWithFaceAuthRefactorFlagDisabled() = + fun everythingEmitsADefaultValueAndDoesNotErrorOut() = testScope.runTest { - featureFlags.set(FACE_AUTH_REFACTOR, false) - underTest = createDeviceEntryFaceAuthRepositoryImpl() initCollectors() // Collecting any flows exposed in the public API doesn't throw any error - authStatus() - detectStatus() - authRunning() - bypassEnabled() - lockedOut() - canFaceAuthRun() - authenticated() + assertThat(authStatus()).isNull() + assertThat(detectStatus()).isNull() + assertThat(authRunning()).isNotNull() + assertThat(bypassEnabled()).isNotNull() + assertThat(lockedOut()).isNotNull() + assertThat(canFaceAuthRun()).isNotNull() + assertThat(authenticated()).isNotNull() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index f2de8caef176..b3e8fed24c3b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -41,8 +41,6 @@ import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.logcatLogBuffer -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository @@ -109,8 +107,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { val scheduler = TestCoroutineScheduler() val dispatcher = StandardTestDispatcher(scheduler) testScope = TestScope(dispatcher) - val featureFlags = FakeFeatureFlags() - featureFlags.set(Flags.FACE_AUTH_REFACTOR, true) bouncerRepository = FakeKeyguardBouncerRepository() faceAuthRepository = FakeDeviceEntryFaceAuthRepository() keyguardTransitionRepository = FakeKeyguardTransitionRepository() @@ -135,21 +131,24 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { testScope.backgroundScope, dispatcher, faceAuthRepository, - PrimaryBouncerInteractor( - bouncerRepository, - mock(BouncerView::class.java), - mock(Handler::class.java), - mock(KeyguardStateController::class.java), - mock(KeyguardSecurityModel::class.java), - mock(PrimaryBouncerCallbackInteractor::class.java), - mock(FalsingCollector::class.java), - mock(DismissCallbackRegistry::class.java), - context, - keyguardUpdateMonitor, - FakeTrustRepository(), - testScope.backgroundScope, - mSelectedUserInteractor, - ), + { + PrimaryBouncerInteractor( + bouncerRepository, + mock(BouncerView::class.java), + mock(Handler::class.java), + mock(KeyguardStateController::class.java), + mock(KeyguardSecurityModel::class.java), + mock(PrimaryBouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), + context, + keyguardUpdateMonitor, + FakeTrustRepository(), + testScope.backgroundScope, + mSelectedUserInteractor, + underTest, + ) + }, AlternateBouncerInteractor( mock(StatusBarStateController::class.java), mock(KeyguardStateController::class.java), @@ -161,7 +160,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { testScope.backgroundScope, ), keyguardTransitionInteractor, - featureFlags, FaceAuthenticationLogger(logcatLogBuffer("faceAuthBuffer")), keyguardUpdateMonitor, fakeDeviceEntryFingerprintAuthRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index ad2ec72468ad..706f94e412ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -24,8 +24,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel import com.android.systemui.power.domain.interactor.PowerInteractorFactory @@ -54,7 +52,6 @@ class KeyguardInteractorTest : SysuiTestCase() { private val repository = testUtils.keyguardRepository private val sceneInteractor = testUtils.sceneInteractor() private val commandQueue = FakeCommandQueue() - private val featureFlags = FakeFeatureFlagsClassic().apply { set(FACE_AUTH_REFACTOR, true) } private val bouncerRepository = FakeKeyguardBouncerRepository() private val configurationRepository = FakeConfigurationRepository() private val shadeRepository = FakeShadeRepository() @@ -66,7 +63,6 @@ class KeyguardInteractorTest : SysuiTestCase() { repository = repository, commandQueue = commandQueue, powerInteractor = PowerInteractorFactory.create().powerInteractor, - featureFlags = featureFlags, sceneContainerFlags = testUtils.sceneContainerFlags, bouncerRepository = bouncerRepository, configurationRepository = configurationRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index fe474faa877d..66c8a229f0a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -31,7 +31,6 @@ import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dock.DockManagerFake import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory @@ -302,10 +301,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { dumpManager = mock(), userHandle = UserHandle.SYSTEM, ) - val featureFlags = - FakeFeatureFlags().apply { - set(Flags.FACE_AUTH_REFACTOR, true) - } + val featureFlags = FakeFeatureFlags() val testDispatcher = StandardTestDispatcher() testScope = TestScope(testDispatcher) underTest = @@ -357,7 +353,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { } underTest.onQuickAffordanceTriggered( - configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::${key}", + configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::$key", expandable = expandable, slotId = "", ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 347d580abf5b..bc4bae0ed959 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -22,7 +22,6 @@ import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.common.shared.model.ContentDescription @@ -31,7 +30,6 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dock.DockManager import com.android.systemui.dock.DockManagerFake import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory @@ -48,6 +46,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots @@ -102,9 +101,11 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { overrideResource( R.array.config_keyguardQuickAffordanceDefaults, arrayOf( - KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + ":" + + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + + ":" + BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, - KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + ":" + + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + + ":" + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET ) ) @@ -168,10 +169,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { dumpManager = mock(), userHandle = UserHandle.SYSTEM, ) - featureFlags = - FakeFeatureFlags().apply { - set(Flags.FACE_AUTH_REFACTOR, true) - } + featureFlags = FakeFeatureFlags() val withDeps = KeyguardInteractorFactory.create( @@ -216,9 +214,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { assertThat(collectedValue()) .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible - assertThat(visibleModel.configKey).isEqualTo( - "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${configKey}" - ) + assertThat(visibleModel.configKey) + .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::$configKey") assertThat(visibleModel.icon).isEqualTo(ICON) assertThat(visibleModel.icon.contentDescription) .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) @@ -243,9 +240,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { assertThat(collectedValue()) .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible - assertThat(visibleModel.configKey).isEqualTo( - "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END}::${configKey}" - ) + assertThat(visibleModel.configKey) + .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END}::$configKey") assertThat(visibleModel.icon).isEqualTo(ICON) assertThat(visibleModel.icon.contentDescription) .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) @@ -364,9 +360,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { assertThat(collectedValue()) .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible - assertThat(visibleModel.configKey).isEqualTo( - "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${configKey}" - ) + assertThat(visibleModel.configKey) + .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::$configKey") assertThat(visibleModel.icon).isEqualTo(ICON) assertThat(visibleModel.icon.contentDescription) .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index bf23bf875ad3..bf6d5c4535ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -121,11 +121,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN) - featureFlags = - FakeFeatureFlags().apply { - set(Flags.FACE_AUTH_REFACTOR, true) - set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) - } + featureFlags = FakeFeatureFlags().apply { set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) } keyguardInteractor = createKeyguardInteractor() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt index 91e17059bb3d..1f245f1e86f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt @@ -32,7 +32,6 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -103,7 +102,7 @@ class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() { keyguardRepository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() configurationRepository = FakeConfigurationRepository() - featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } + featureFlags = FakeFeatureFlags() trustRepository = FakeTrustRepository() powerRepository = FakePowerRepository() powerInteractor = @@ -147,7 +146,8 @@ class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() { keyguardUpdateMonitor, trustRepository, testScope.backgroundScope, - mSelectedUserInteractor + mSelectedUserInteractor, + keyguardFaceAuthInteractor = mock(), ), AlternateBouncerInteractor( statusBarStateController = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt index 16d072e99964..87eee1a8d817 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt @@ -25,8 +25,6 @@ import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.doze.util.BurnInHelperWrapper -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.shared.model.StatusBarState @@ -64,7 +62,6 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() { private lateinit var bouncerRepository: KeyguardBouncerRepository private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var fakeCommandQueue: FakeCommandQueue - private lateinit var featureFlags: FakeFeatureFlags private lateinit var burnInInteractor: BurnInInteractor private lateinit var shadeRepository: FakeShadeRepository private lateinit var keyguardInteractor: KeyguardInteractor @@ -80,8 +77,7 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) testScope = TestScope() configRepository = FakeConfigurationRepository() - featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } - KeyguardInteractorFactory.create(featureFlags = featureFlags).let { + KeyguardInteractorFactory.create().let { keyguardInteractor = it.keyguardInteractor keyguardRepository = it.repository } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index 32d28a3d7bb2..1584be0ab565 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -22,7 +22,6 @@ import android.os.UserHandle import androidx.test.filters.SmallTest import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.widget.LockPatternUtils -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable @@ -52,6 +51,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots @@ -116,9 +116,11 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { overrideResource( R.array.config_keyguardQuickAffordanceDefaults, arrayOf( - KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + ":" + + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + + ":" + BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, - KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + ":" + + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + + ":" + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET ) ) @@ -138,7 +140,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { biometricSettingsRepository = FakeBiometricSettingsRepository() val featureFlags = FakeFeatureFlags().apply { - set(Flags.FACE_AUTH_REFACTOR, true) set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 25d141997734..67c4e2688cd0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -22,14 +22,13 @@ import android.content.Intent import android.os.UserHandle import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils -import com.android.systemui.res.R +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dock.DockManagerFake -import com.android.systemui.Flags as AConfigFlags import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys @@ -49,6 +48,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots @@ -129,7 +129,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { val featureFlags = FakeFeatureFlags().apply { - set(Flags.FACE_AUTH_REFACTOR, true) set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index a838684a4f41..e6d6cf263d69 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -32,9 +32,7 @@ import com.android.systemui.common.ui.data.repository.FakeConfigurationRepositor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository -import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.FakeFeatureFlagsClassicModule -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.BurnInInteractor @@ -109,9 +107,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) - val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, true) } - - val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags) + val withDeps = KeyguardInteractorFactory.create() keyguardInteractor = withDeps.keyguardInteractor repository = withDeps.repository configurationRepository = withDeps.configurationRepository @@ -135,7 +131,6 @@ class KeyguardRootViewModelTest : SysuiTestCase() { deviceEntryInteractor = mock { whenever(isBypassEnabled).thenReturn(MutableStateFlow(false)) }, dozeParameters = mock(), - featureFlags, keyguardInteractor, keyguardTransitionInteractor, notificationsKeyguardInteractor = @@ -347,8 +342,7 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() { DaggerKeyguardRootViewModelTestWithFakes_TestComponent.factory() .create( test = this, - featureFlags = - FakeFeatureFlagsClassicModule { set(Flags.FACE_AUTH_REFACTOR, true) }, + featureFlags = FakeFeatureFlagsClassicModule(), mocks = TestMocksModule( dozeParameters = dozeParams, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt index c50be04e8a9c..2314c8358ff8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt @@ -28,7 +28,6 @@ import com.android.systemui.collectValues import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository import com.android.systemui.flags.FakeFeatureFlagsClassicModule -import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -94,10 +93,7 @@ class LockscreenToAodTransitionViewModelTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(FACE_AUTH_REFACTOR, true) - set(FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(FULL_SCREEN_USER_SWITCHER, true) }, mocks = TestMocksModule(), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt index 26704da1496f..baac51385ba1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -85,10 +85,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, true) - set(Flags.FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, mocks = TestMocksModule(), ) @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt index ff3135a6ad98..29d8f082795a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -85,10 +85,7 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, true) - set(Flags.FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, mocks = TestMocksModule(), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt index 8afd8e4fd425..049e4e27e374 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt @@ -86,10 +86,7 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, true) - set(Flags.FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, mocks = TestMocksModule(), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt index 5058b1686781..6512290bf556 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt @@ -23,8 +23,6 @@ import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.doze.util.BurnInHelperWrapper -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.BurnInInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor @@ -54,7 +52,6 @@ class UdfpsAodViewModelTest : SysuiTestCase() { private lateinit var configRepository: FakeConfigurationRepository private lateinit var bouncerRepository: KeyguardBouncerRepository private lateinit var keyguardRepository: FakeKeyguardRepository - private lateinit var featureFlags: FakeFeatureFlags private lateinit var shadeRepository: FakeShadeRepository private lateinit var keyguardInteractor: KeyguardInteractor @@ -67,16 +64,12 @@ class UdfpsAodViewModelTest : SysuiTestCase() { overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding) testScope = TestScope() shadeRepository = FakeShadeRepository() - featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } - KeyguardInteractorFactory.create( - featureFlags = featureFlags, - ) - .also { - keyguardInteractor = it.keyguardInteractor - keyguardRepository = it.repository - configRepository = it.configurationRepository - bouncerRepository = it.bouncerRepository - } + KeyguardInteractorFactory.create().also { + keyguardInteractor = it.keyguardInteractor + keyguardRepository = it.repository + configRepository = it.configurationRepository + bouncerRepository = it.bouncerRepository + } val udfpsKeyguardInteractor = UdfpsKeyguardInteractor( configRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt index f039f5302a3f..95b2fe554f0c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt @@ -24,8 +24,6 @@ import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.doze.util.BurnInHelperWrapper -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -59,7 +57,6 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() { private lateinit var bouncerRepository: KeyguardBouncerRepository private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var fakeCommandQueue: FakeCommandQueue - private lateinit var featureFlags: FakeFeatureFlags private lateinit var transitionRepository: FakeKeyguardTransitionRepository private lateinit var shadeRepository: FakeShadeRepository @@ -75,14 +72,12 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() { keyguardRepository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() fakeCommandQueue = FakeCommandQueue() - featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } bouncerRepository = FakeKeyguardBouncerRepository() transitionRepository = FakeKeyguardTransitionRepository() shadeRepository = FakeShadeRepository() val keyguardInteractor = KeyguardInteractorFactory.create( repository = keyguardRepository, - featureFlags = featureFlags, ) .keyguardInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt index c1805dbf26ad..848a94b2a5d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt @@ -23,8 +23,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.BurnInInteractor @@ -75,7 +73,6 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() { private lateinit var keyguardInteractor: KeyguardInteractor private lateinit var bouncerRepository: FakeKeyguardBouncerRepository private lateinit var shadeRepository: FakeShadeRepository - private lateinit var featureFlags: FakeFeatureFlags @Before fun setUp() { @@ -83,16 +80,12 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() { testScope = TestScope() transitionRepository = FakeKeyguardTransitionRepository() shadeRepository = FakeShadeRepository() - featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } - KeyguardInteractorFactory.create( - featureFlags = featureFlags, - ) - .also { - keyguardInteractor = it.keyguardInteractor - keyguardRepository = it.repository - configRepository = it.configurationRepository - bouncerRepository = it.bouncerRepository - } + KeyguardInteractorFactory.create().also { + keyguardInteractor = it.keyguardInteractor + keyguardRepository = it.repository + configRepository = it.configurationRepository + bouncerRepository = it.bouncerRepository + } val transitionInteractor = KeyguardTransitionInteractorFactory.create( diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 5c325ae67369..42e27ba12f42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags @@ -39,14 +38,11 @@ import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsPro import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class QuickSettingsSceneViewModelTest : SysuiTestCase() { @@ -90,15 +86,8 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { broadcastDispatcher = fakeBroadcastDispatcher, ) - val authenticationInteractor = utils.authenticationInteractor() - underTest = QuickSettingsSceneViewModel( - deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ), shadeHeaderViewModel = shadeHeaderViewModel, qsSceneAdapter = qsFlexiglassAdapter, notifications = utils.notificationsPlaceholderViewModel(), @@ -106,32 +95,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { } @Test - fun onContentClicked_deviceUnlocked_switchesToGone() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.deviceEntryRepository.setUnlocked(true) - runCurrent() - - underTest.onContentClicked() - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - - @Test - fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.deviceEntryRepository.setUnlocked(false) - runCurrent() - - underTest.onContentClicked() - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - } - - @Test fun destinationsNotCustomizing() = testScope.runTest { val destinations by collectLastValue(underTest.destinationScenes) 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 722fb2c75081..36b4435e2137 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -31,7 +31,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; @@ -56,7 +55,6 @@ import android.view.accessibility.AccessibilityNodeInfo; import androidx.constraintlayout.widget.ConstraintSet; import androidx.test.filters.SmallTest; -import com.android.keyguard.FaceAuthApiRequestReason; import com.android.systemui.DejankUtils; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; @@ -1075,33 +1073,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked(); - verify(mUpdateMonitor).requestFaceAuth( - FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED); - } - - @Test - public void onEmptySpaceClicked_whenDozingAndOnKeyguard_doesNotRequestFaceAuth() { - StatusBarStateController.StateListener statusBarStateListener = - mNotificationPanelViewController.getStatusBarStateListener(); - statusBarStateListener.onStateChanged(KEYGUARD); - mNotificationPanelViewController.setDozing(true, false); - - // This sets the dozing state that is read when onMiddleClicked is eventually invoked. - mTouchHandler.onTouch(mock(View.class), mDownMotionEvent); - mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); - - verify(mUpdateMonitor, never()).requestFaceAuth(anyString()); - } - - @Test - public void onEmptySpaceClicked_whenStatusBarShadeLocked_doesNotRequestFaceAuth() { - StatusBarStateController.StateListener statusBarStateListener = - mNotificationPanelViewController.getStatusBarStateListener(); - statusBarStateListener.onStateChanged(SHADE_LOCKED); - - mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); - - verify(mUpdateMonitor, never()).requestFaceAuth(anyString()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 8403ac59d2f5..39b306b781f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -188,7 +188,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { keyguardRepository, new FakeCommandQueue(), powerInteractor, - featureFlags, sceneContainerFlags, new FakeKeyguardBouncerRepository(), configurationRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index d89491c7b442..0587633b09cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -65,6 +65,7 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.TransitionStep @@ -97,6 +98,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.TestScope @@ -111,9 +113,8 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import java.util.Optional import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -265,6 +266,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, + mock(KeyguardFaceAuthInteractor::class.java) ), facePropertyRepository = FakeFacePropertyRepository(), deviceEntryFingerprintAuthRepository = diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 9c8816c72fae..29b1366eb6c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -251,6 +251,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, + mock(), ), facePropertyRepository = FakeFacePropertyRepository(), deviceEntryFingerprintAuthRepository = diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index bff47f1e3927..1dbb2972c6f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -224,7 +224,6 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mKeyguardRepository, new FakeCommandQueue(), powerInteractor, - featureFlags, sceneContainerFlags, new FakeKeyguardBouncerRepository(), configurationRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt index 61e4370949ca..65e0fa146fe3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt @@ -105,10 +105,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, false) - set(Flags.FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, mocks = TestMocksModule( dozeParameters = dozeParameters, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt index 92eb6ed52c14..6e6e4384b7b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt @@ -86,10 +86,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, false) - set(Flags.FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, mocks = TestMocksModule( dozeParameters = dozeParameters, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt index 729f3f840e1a..565e20a034db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt @@ -91,10 +91,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { .create( test = this, featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, false) - set(Flags.FULL_SCREEN_USER_SWITCHER, true) - }, + FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, mocks = TestMocksModule( dozeParameters = dozeParameters, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java index 63e46d1895bd..459040abd566 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; -import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; @@ -56,7 +55,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.logging.KeyguardLogger; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; @@ -72,6 +70,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; @@ -249,7 +248,6 @@ public class KeyguardIndicationControllerBaseTest extends SysuiTestCase { mFlags = new FakeFeatureFlags(); mFlags.set(KEYGUARD_TALKBACK_FIX, true); mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); - mFlags.set(FACE_AUTH_REFACTOR, false); mController = new KeyguardIndicationController( mContext, mTestableLooper.getLooper(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index dd3ac927ebdf..aa53558e858d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -479,7 +479,7 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll createController(); // GIVEN face has already unlocked the device - when(mKeyguardUpdateMonitor.getUserUnlockedWithFace(anyInt())).thenReturn(true); + when(mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()).thenReturn(true); String message = "A message"; mController.setVisible(true); @@ -586,7 +586,7 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll createController(); String message = mContext.getString(R.string.keyguard_retry); when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); - when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true); + when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(true); when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(false); mController.setVisible(true); @@ -602,7 +602,7 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll String message = mContext.getString(R.string.keyguard_retry); when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true); - when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true); + when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(true); mController.setVisible(true); mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index 8fa7cd291851..7546dfa1cbf9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -66,8 +66,8 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @@ -93,88 +93,98 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { whenever(interactionJankMonitor.end(anyInt())).thenReturn(true) uiEventLogger = UiEventLoggerFake() - controller = object : StatusBarStateControllerImpl( - uiEventLogger, - interactionJankMonitor, - mock(), - { shadeInteractor } - ) { - override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator } - } - - val powerInteractor = PowerInteractor( - FakePowerRepository(), - FalsingCollectorFake(), - mock(), - controller) + controller = + object : + StatusBarStateControllerImpl( + uiEventLogger, + interactionJankMonitor, + mock(), + { shadeInteractor } + ) { + override fun createDarkAnimator(): ObjectAnimator { + return mockDarkAnimator + } + } + + val powerInteractor = + PowerInteractor(FakePowerRepository(), FalsingCollectorFake(), mock(), controller) val keyguardRepository = FakeKeyguardRepository() val keyguardTransitionRepository = FakeKeyguardTransitionRepository() val featureFlags = FakeFeatureFlagsClassic() val shadeRepository = FakeShadeRepository() val sceneContainerFlags = FakeSceneContainerFlags() val configurationRepository = FakeConfigurationRepository() - val keyguardInteractor = KeyguardInteractor( - keyguardRepository, - FakeCommandQueue(), - powerInteractor, - featureFlags, - sceneContainerFlags, - FakeKeyguardBouncerRepository(), - configurationRepository, - shadeRepository, - utils::sceneInteractor) - val keyguardTransitionInteractor = KeyguardTransitionInteractor( - testScope.backgroundScope, - keyguardTransitionRepository, - { keyguardInteractor }, - { fromLockscreenTransitionInteractor }, - { fromPrimaryBouncerTransitionInteractor }) - fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor( - keyguardTransitionRepository, - keyguardTransitionInteractor, - testScope.backgroundScope, - keyguardInteractor, - featureFlags, - shadeRepository, - powerInteractor, - { - InWindowLauncherUnlockAnimationInteractor( - InWindowLauncherUnlockAnimationRepository(), - testScope, - keyguardTransitionInteractor, - { FakeKeyguardSurfaceBehindRepository() }, - mock(), - ) - }) - fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor( - keyguardTransitionRepository, - keyguardTransitionInteractor, - testScope.backgroundScope, - keyguardInteractor, - featureFlags, - mock(), - mock(), - powerInteractor) - shadeInteractor = ShadeInteractorImpl( - testScope.backgroundScope, - FakeDeviceProvisioningRepository(), - FakeDisableFlagsRepository(), - mock(), - keyguardRepository, - keyguardTransitionInteractor, - powerInteractor, - FakeUserSetupRepository(), - mock(), - ShadeInteractorLegacyImpl( - testScope.backgroundScope, + val keyguardInteractor = + KeyguardInteractor( keyguardRepository, - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController()), + FakeCommandQueue(), + powerInteractor, + sceneContainerFlags, + FakeKeyguardBouncerRepository(), + configurationRepository, shadeRepository, + utils::sceneInteractor + ) + val keyguardTransitionInteractor = + KeyguardTransitionInteractor( + testScope.backgroundScope, + keyguardTransitionRepository, + { keyguardInteractor }, + { fromLockscreenTransitionInteractor }, + { fromPrimaryBouncerTransitionInteractor } + ) + fromLockscreenTransitionInteractor = + FromLockscreenTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + testScope.backgroundScope, + keyguardInteractor, + featureFlags, + shadeRepository, + powerInteractor, + { + InWindowLauncherUnlockAnimationInteractor( + InWindowLauncherUnlockAnimationRepository(), + testScope, + keyguardTransitionInteractor, + { FakeKeyguardSurfaceBehindRepository() }, + mock(), + ) + } + ) + fromPrimaryBouncerTransitionInteractor = + FromPrimaryBouncerTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + testScope.backgroundScope, + keyguardInteractor, + featureFlags, + mock(), + mock(), + powerInteractor + ) + shadeInteractor = + ShadeInteractorImpl( + testScope.backgroundScope, + FakeDeviceProvisioningRepository(), + FakeDisableFlagsRepository(), + mock(), + keyguardRepository, + keyguardTransitionInteractor, + powerInteractor, + FakeUserSetupRepository(), + mock(), + ShadeInteractorLegacyImpl( + testScope.backgroundScope, + keyguardRepository, + SharedNotificationContainerInteractor( + configurationRepository, + mContext, + ResourcesSplitShadeStateController() + ), + shadeRepository, + ) ) - ) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt index d1a46fca21c8..057dcb2a156e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt @@ -62,7 +62,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { private val ongoingCallRepository = OngoingCallRepository() private val underTest = - StatusBarModeRepositoryImpl( + StatusBarModePerDisplayRepositoryImpl( testScope.backgroundScope, DISPLAY_ID, commandQueue, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt index 10d4c627bc4a..b3fc25c47912 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt @@ -93,7 +93,6 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { test = this, featureFlags = FakeFeatureFlagsClassicModule { - setDefault(Flags.FACE_AUTH_REFACTOR) set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) }, mocks = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt index e264fc07489a..741564505b53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -105,7 +105,6 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { test = this, featureFlags = FakeFeatureFlagsClassicModule { - setDefault(Flags.FACE_AUTH_REFACTOR) set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) }, mocks = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt index 1c7fd565c289..7361f6baa7b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.interruption import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE @@ -37,7 +36,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() { init { - setFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR) + mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME) } override val provider by lazy { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt index df6f0d716577..d2c046c67fb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.interruption import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK @@ -30,7 +29,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() { init { - setFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR) + mSetFlagsRule.enableFlags(VisualInterruptionRefactor.FLAG_NAME) } override val provider by lazy { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt index 7babff5e0b2e..2ac0cb7499d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt @@ -44,7 +44,6 @@ import android.graphics.drawable.Icon import android.hardware.display.FakeAmbientDisplayConfiguration import android.os.Looper import android.os.PowerManager -import android.platform.test.flag.junit.SetFlagsRule import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED import android.provider.Settings.Global.HEADS_UP_OFF import android.provider.Settings.Global.HEADS_UP_ON @@ -84,15 +83,10 @@ import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Assert.assertEquals import org.junit.Before -import org.junit.Rule import org.junit.Test import org.mockito.Mockito.`when` as whenever abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { - @JvmField - @Rule - val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT) - private val fakeLogBuffer = LogBuffer( name = "FakeLog", diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index e61b4f81aaee..051a4c1c05cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -135,7 +135,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); - when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true); + when(mKeyguardStateController.isFaceEnrolledAndEnabled()).thenReturn(true); when(mKeyguardStateController.isUnlocked()).thenReturn(false); when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean())) .thenReturn(true); 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 4b1c7e8faa38..4422764c4bac 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 @@ -157,6 +157,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; @@ -347,6 +348,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); when(mDozeParameters.getAlwaysOn()).thenReturn(true); mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); + // TODO: b/312476335 - Update to check flag and instantiate old or new implementation. + mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME); IThermalService thermalService = mock(IThermalService.class); mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt index bd0dbeebd752..91cbc3274900 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt @@ -86,7 +86,7 @@ class KeyguardBypassControllerTest : SysuiTestCase() { featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true) - whenever(keyguardStateController.isFaceEnrolled).thenReturn(true) + whenever(keyguardStateController.isFaceEnrolledAndEnabled).thenReturn(true) } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 361df1c63ffd..62a2bc54af20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -60,7 +60,6 @@ import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.domain.interactor.PowerInteractorFactory; import com.android.systemui.res.R; import com.android.systemui.scene.SceneTestUtils; @@ -166,7 +165,6 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mKeyguardRepository, mCommandQueue, PowerInteractorFactory.create().getPowerInteractor(), - mFeatureFlags, mSceneTestUtils.getSceneContainerFlags(), new FakeKeyguardBouncerRepository(), new FakeConfigurationRepository(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java index 287ebba4db24..bde2243db4e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java @@ -54,7 +54,7 @@ import java.util.Objects; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper -public class LightsOutNotifControllerTest extends SysuiTestCase { +public class LegacyLightsOutNotifControllerTest extends SysuiTestCase { private static final int LIGHTS_ON = 0; private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS; @@ -68,7 +68,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksCaptor; private View mLightsOutView; - private LightsOutNotifController mLightsOutNotifController; + private LegacyLightsOutNotifController mLightsOutNotifController; private int mDisplayId; private Observer<Boolean> mHaActiveNotifsObserver; private CommandQueue.Callbacks mCallbacks; @@ -83,7 +83,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { when(mNotifLiveDataStore.getHasActiveNotifs()).thenReturn(mHasActiveNotifs); when(mHasActiveNotifs.getValue()).thenReturn(false); - mLightsOutNotifController = new LightsOutNotifController( + mLightsOutNotifController = new LegacyLightsOutNotifController( mLightsOutView, mWindowManager, mNotifLiveDataStore, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java index c45ecf3fe225..f6419a9c4784 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java @@ -121,7 +121,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -142,7 +142,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(0 /* appearance */, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -165,7 +165,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -190,7 +190,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, thirdBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -214,7 +214,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(0 /* appearance */, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -231,7 +231,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1)) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -249,7 +249,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(0, new Rect(0, 0, 1, 1)) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -266,7 +266,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1)) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -276,7 +276,7 @@ public class LightBarControllerTest extends SysuiTestCase { reset(mStatusBarIconController); // WHEN the same appearance regions but different status bar mode is sent - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.LIGHTS_OUT_TRANSPARENT, STATUS_BAR_BOUNDS, @@ -298,7 +298,7 @@ public class LightBarControllerTest extends SysuiTestCase { /* start= */ new Rect(0, 0, 10, 10), /* end= */ new Rect(0, 0, 20, 20)); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, startingBounds, @@ -311,7 +311,7 @@ public class LightBarControllerTest extends SysuiTestCase { BoundsPair newBounds = new BoundsPair( /* start= */ new Rect(0, 0, 30, 30), /* end= */ new Rect(0, 0, 40, 40)); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, newBounds, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt new file mode 100644 index 000000000000..5a0e13d02b92 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 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.phone.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.data.model.StatusBarMode +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@SmallTest +class LightsOutInteractorTest : SysuiTestCase() { + + private val statusBarModeRepository = FakeStatusBarModeRepository() + private val interactor: LightsOutInteractor = LightsOutInteractor(statusBarModeRepository) + + @Test + fun isLowProfile_lightsOutStatusBarMode_false() = runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT + + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + + assertThat(actual).isTrue() + } + + @Test + fun isLowProfile_lightsOutTransparentStatusBarMode_true() = runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.LIGHTS_OUT_TRANSPARENT + + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + + assertThat(actual).isTrue() + } + + @Test + fun isLowProfile_transparentStatusBarMode_false() = runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + + assertThat(actual).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 0b87fe8da184..17c29382b39a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -46,7 +46,6 @@ import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.common.ui.ConfigurationState; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; import com.android.systemui.plugins.DarkIconDispatcher; @@ -695,7 +694,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mLocationPublisher, mMockNotificationAreaController, mShadeExpansionStateManager, - mock(FeatureFlagsClassic.class), mStatusBarIconController, mIconManagerFactory, mCollapsedStatusBarViewModel, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index 49de5125cb4d..7b73528cd932 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -549,7 +549,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun fullscreenIsTrue_chipStillClickable() { notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() assertThat(chipView.hasOnClickListeners()).isTrue() @@ -559,7 +559,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun callStartedInImmersiveMode_swipeGestureCallbackAdded() { - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) @@ -570,7 +570,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() { - statusBarModeRepository.isInFullscreenMode.value = false + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false testScope.runCurrent() notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) @@ -583,7 +583,7 @@ class OngoingCallControllerTest : SysuiTestCase() { fun transitionToImmersiveMode_swipeGestureCallbackAdded() { notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() verify(mockSwipeStatusBarAwayGestureHandler) @@ -592,11 +592,11 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() { - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) reset(mockSwipeStatusBarAwayGestureHandler) - statusBarModeRepository.isInFullscreenMode.value = false + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false testScope.runCurrent() verify(mockSwipeStatusBarAwayGestureHandler) @@ -605,7 +605,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() { - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() val ongoingCallNotifEntry = createOngoingCallNotifEntry() notifCollectionListener.onEntryAdded(ongoingCallNotifEntry) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index 688f739f61f8..09dc1e537e9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -17,49 +17,77 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import androidx.test.filters.SmallTest +import com.android.systemui.CoroutineTestScopeModule +import com.android.systemui.Flags +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectValues +import com.android.systemui.collectLastValue +import com.android.systemui.collectValues +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.runTest +import com.android.systemui.statusbar.data.model.StatusBarMode +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { - private lateinit var underTest: CollapsedStatusBarViewModel + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent : SysUITestComponent<CollapsedStatusBarViewModelImpl> { + val statusBarModeRepository: FakeStatusBarModeRepository + val activeNotificationListRepository: ActiveNotificationListRepository + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + testScope: CoroutineTestScopeModule, + ): TestComponent + } + } - private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository - private lateinit var testScope: TestScope + @OptIn(ExperimentalCoroutinesApi::class) + private val testComponent: TestComponent = + DaggerCollapsedStatusBarViewModelImplTest_TestComponent.factory() + .create( + test = this, + testScope = CoroutineTestScopeModule(TestScope(UnconfinedTestDispatcher())), + ) @Before fun setUp() { - testScope = TestScope(UnconfinedTestDispatcher()) - - keyguardTransitionRepository = FakeKeyguardTransitionRepository() - val interactor = - KeyguardTransitionInteractorFactory.create( - scope = TestScope().backgroundScope, - repository = keyguardTransitionRepository, - ) - .keyguardTransitionInteractor - underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope) + mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_LIVE_DATA_STORE_REFACTOR) } @Test fun isTransitioningFromLockscreenToOccluded_started_isTrue() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -77,8 +105,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_running_isTrue() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -96,13 +124,13 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_finished_isFalse() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.OCCLUDED, - this.testScheduler, + testScope.testScheduler, ) assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isFalse() @@ -112,8 +140,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_canceled_isFalse() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -131,8 +159,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_irrelevantTransition_isFalse() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -150,8 +178,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_followsRepoUpdates() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -182,7 +210,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_started_emitted() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -199,7 +227,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_startedMultiple_emittedMultiple() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -234,7 +262,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_startedThenRunning_emittedOnlyOne() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -283,7 +311,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransition_notEmitted() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -300,7 +328,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransitionState_notEmitted() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -317,4 +345,65 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(emissions).isEmpty() } + + @Test + fun areNotificationsLightsOut_lowProfileWithNotifications_true() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.LIGHTS_OUT_TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(testNotifications) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isTrue() + } + + @Test + fun areNotificationsLightsOut_lowProfileWithoutNotifications_false() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.LIGHTS_OUT_TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(emptyList()) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isFalse() + } + + @Test + fun areNotificationsLightsOut_defaultStatusBarModeWithoutNotifications_false() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(emptyList()) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isFalse() + } + + @Test + fun areNotificationsLightsOut_defaultStatusBarModeWithNotifications_false() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(testNotifications) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isFalse() + } + + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = + ActiveNotificationsStore.Builder() + .apply { notifications.forEach(::addIndividualNotif) } + .build() + + private val testNotifications = + listOf( + activeNotificationModel(key = "notif1"), + activeNotificationModel(key = "notif2"), + ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt index 88587b2db0f9..bc50f7967403 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt @@ -16,11 +16,20 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { + private val areNotificationLightsOut = MutableStateFlow(false) + override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false) override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>() + + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut + + fun setNotificationLightsOut(lightsOut: Boolean) { + areNotificationLightsOut.value = lightsOut + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java index 5c960b6633db..01dad381efa0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java @@ -100,16 +100,16 @@ public class KeyguardStateControllerTest extends SysuiTestCase { public void testFaceAuthEnrolleddChanged_calledWhenFaceEnrollmentStateChanges() { KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class); - when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(false); + when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(false); verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture()); mKeyguardStateController.addCallback(callback); - assertThat(mKeyguardStateController.isFaceEnrolled()).isFalse(); + assertThat(mKeyguardStateController.isFaceEnrolledAndEnabled()).isFalse(); - when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(true); + when(mKeyguardUpdateMonitor.isFaceEnabledAndEnrolled()).thenReturn(true); mUpdateCallbackCaptor.getValue().onBiometricEnrollmentStateChanged( BiometricSourceType.FACE); - assertThat(mKeyguardStateController.isFaceEnrolled()).isTrue(); + assertThat(mKeyguardStateController.isFaceEnrolledAndEnabled()).isTrue(); verify(callback).onFaceEnrolledChanged(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt index 96db09edaf88..59bf9f30a828 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt @@ -21,7 +21,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.model.StatusBarState @@ -55,7 +54,6 @@ class KeyguardStatusBarViewModelTest : SysuiTestCase() { keyguardRepository, mock<CommandQueue>(), PowerInteractorFactory.create().powerInteractor, - FakeFeatureFlagsClassic(), sceneTestUtils.sceneContainerFlags, FakeKeyguardBouncerRepository(), FakeConfigurationRepository(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt index 7f990a446aaf..f9241343549c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt @@ -26,7 +26,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory @@ -106,8 +105,7 @@ class FoldAodAnimationControllerTest : SysuiTestCase() { onActionStarted.run() } - val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) } - val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags) + val withDeps = KeyguardInteractorFactory.create(featureFlags = FakeFeatureFlags()) val keyguardInteractor = withDeps.keyguardInteractor keyguardRepository = withDeps.repository diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt index 0d78ae9c7b11..abfff34f6dba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt @@ -23,8 +23,6 @@ import android.os.UserManager import android.provider.Settings import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.settings.FakeUserTracker import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus @@ -322,8 +320,6 @@ class UserRepositoryImplTest : SysuiTestCase() { } private fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl { - val featureFlags = FakeFeatureFlags() - featureFlags.set(FACE_AUTH_REFACTOR, true) return UserRepositoryImpl( appContext = context, manager = manager, @@ -332,7 +328,6 @@ class UserRepositoryImplTest : SysuiTestCase() { backgroundDispatcher = IMMEDIATE, globalSettings = globalSettings, tracker = tracker, - featureFlags = featureFlags, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt index 017eefe38f2a..bf851eb69a0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt @@ -121,8 +121,6 @@ class UserSwitcherInteractorTest : SysuiTestCase() { ) utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false) - utils.featureFlags.set(Flags.FACE_AUTH_REFACTOR, true) - spyContext = spy(context) keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.featureFlags) keyguardRepository = keyguardReply.repository diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt index 7041eab9d247..d1870b1d8fcd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt @@ -233,11 +233,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { } private fun viewModel(): StatusBarUserChipViewModel { - val featureFlags = - FakeFeatureFlags().apply { - set(Flags.FULL_SCREEN_USER_SWITCHER, false) - set(Flags.FACE_AUTH_REFACTOR, true) - } + val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } runBlocking { userRepository.setUserInfos(listOf(USER_0)) userRepository.setSelectedUserInfo(USER_0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index 686f492fde50..b7b24f6dc7dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -147,11 +147,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { resetOrExitSessionReceiver = resetOrExitSessionReceiver, ) - val featureFlags = - FakeFeatureFlags().apply { - set(Flags.FULL_SCREEN_USER_SWITCHER, false) - set(Flags.FACE_AUTH_REFACTOR, true) - } + val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags) keyguardRepository = reply.repository 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 df7609c544a4..ca167ad3cd14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -146,6 +146,7 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper; +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor; @@ -366,6 +367,9 @@ public class BubblesTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + // TODO: b/312476335 - Update to check flag and instantiate old or new implementation. + mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { doReturn(true).when(mTransitions).isRegistered(); } @@ -411,7 +415,6 @@ public class BubblesTest extends SysuiTestCase { keyguardRepository, new FakeCommandQueue(), powerInteractor, - featureFlags, sceneContainerFlags, new FakeKeyguardBouncerRepository(), configurationRepository, diff --git a/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt new file mode 100644 index 000000000000..f9c920a89e16 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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 + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.activityManager by Kosmos.Fixture { mock<ActivityManager>() } diff --git a/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt new file mode 100644 index 000000000000..b284ac0ec7e9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.admin + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.devicePolicyManager by Kosmos.Fixture { mock<DevicePolicyManager>() } diff --git a/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt new file mode 100644 index 000000000000..f96c5080bb95 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.content + +import com.android.systemui.SysuiTestableContext +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase + +val Kosmos.testableContext: SysuiTestableContext by Kosmos.Fixture { testCase.context } +var Kosmos.applicationContext: Context by Kosmos.Fixture { testableContext } diff --git a/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt new file mode 100644 index 000000000000..56867640d03d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.content.res + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +var Kosmos.mainResources: Resources by Kosmos.Fixture { applicationContext.resources } diff --git a/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt new file mode 100644 index 000000000000..c936b914f44e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.os + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.userManager by Kosmos.Fixture { mock<UserManager>() } diff --git a/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt new file mode 100644 index 000000000000..34c0a7915523 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.view + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +val Kosmos.layoutInflater: LayoutInflater by + Kosmos.Fixture { LayoutInflater.from(applicationContext) } diff --git a/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt new file mode 100644 index 000000000000..9059da2259b1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.internal.logging + +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.kosmos.Kosmos + +var Kosmos.uiEventLogger: UiEventLogger by Kosmos.Fixture { uiEventLoggerFake } +val Kosmos.uiEventLoggerFake by Kosmos.Fixture { UiEventLoggerFake() } diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt new file mode 100644 index 000000000000..fadcecc07190 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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 + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.keyguardSecurityModel by Kosmos.Fixture { mock<KeyguardSecurityModel>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt new file mode 100644 index 000000000000..b32cbe6da0ec --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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 + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.keyguardUpdateMonitor by Kosmos.Fixture { mock<KeyguardUpdateMonitor>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt new file mode 100644 index 000000000000..4c4cfd541a1f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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 + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.guestResetOrExitSessionReceiver by + Kosmos.Fixture { mock<GuestResetOrExitSessionReceiver>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt new file mode 100644 index 000000000000..a9855ffda8b8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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 + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.guestResumeSessionReceiver by Kosmos.Fixture { mock<GuestResumeSessionReceiver>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt new file mode 100644 index 000000000000..ea93e949c83c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 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.authentication.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import kotlinx.coroutines.test.currentTime + +var Kosmos.authenticationRepository: AuthenticationRepository by + Kosmos.Fixture { fakeAuthenticationRepository } +val Kosmos.fakeAuthenticationRepository by + Kosmos.Fixture { FakeAuthenticationRepository { testScope.currentTime } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt new file mode 100644 index 000000000000..060ca4c7e912 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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.authentication.domain.interactor + +import com.android.systemui.authentication.data.repository.authenticationRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.user.data.repository.userRepository +import com.android.systemui.util.time.fakeSystemClock + +val Kosmos.authenticationInteractor by + Kosmos.Fixture { + AuthenticationInteractor( + applicationScope = applicationCoroutineScope, + repository = authenticationRepository, + backgroundDispatcher = testDispatcher, + userRepository = userRepository, + clock = fakeSystemClock, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt new file mode 100644 index 000000000000..2a870749644a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.bouncer.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.keyguardBouncerRepository: KeyguardBouncerRepository by + Kosmos.Fixture { fakeKeyguardBouncerRepository } +val Kosmos.fakeKeyguardBouncerRepository by Kosmos.Fixture { FakeKeyguardBouncerRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt new file mode 100644 index 000000000000..3a72d11cdc18 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.classifier + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.falsingCollector by Kosmos.Fixture { FalsingCollectorFake() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt new file mode 100644 index 000000000000..86a8ae5f9cf4 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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.common.ui + +import android.content.applicationContext +import android.view.layoutInflater +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.configurationController + +val Kosmos.configurationState: ConfigurationState by + Kosmos.Fixture { + ConfigurationState(configurationController, applicationContext, layoutInflater) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt new file mode 100644 index 000000000000..77b8bd4ff2de --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.common.ui.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.configurationRepository: ConfigurationRepository by + Kosmos.Fixture { fakeConfigurationRepository } +val Kosmos.fakeConfigurationRepository by Kosmos.Fixture { FakeConfigurationRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt index b27926c8ba50..faacce64b2e4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt @@ -65,6 +65,7 @@ object CommunalInteractorFactory { widgetRepository, mediaRepository, smartspaceRepository, + withDeps.keyguardInteractor, appWidgetHost, editWidgetsActivityStarter, ), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt new file mode 100644 index 000000000000..3da0681dc45f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.deviceentry.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceEntryRepository: DeviceEntryRepository by + Kosmos.Fixture { fakeDeviceEntryRepository } +val Kosmos.fakeDeviceEntryRepository by Kosmos.Fixture { FakeDeviceEntryRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt new file mode 100644 index 000000000000..b600b50b8d2d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.authentication.domain.interactor.authenticationInteractor +import com.android.systemui.deviceentry.data.repository.deviceEntryRepository +import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.trustRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.sceneContainerFlags +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.deviceEntryInteractor by + Kosmos.Fixture { + DeviceEntryInteractor( + applicationScope = applicationCoroutineScope, + repository = deviceEntryRepository, + authenticationInteractor = authenticationInteractor, + sceneInteractor = sceneInteractor, + deviceEntryFaceAuthRepository = deviceEntryFaceAuthRepository, + trustRepository = trustRepository, + flags = sceneContainerFlags, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt index a78076338c79..e6b7f62c7d5f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt @@ -18,4 +18,4 @@ package com.android.systemui.flags import com.android.systemui.kosmos.Kosmos -val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() } +val Kosmos.featureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt new file mode 100644 index 000000000000..3d729675f7ab --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository by + Kosmos.Fixture { fakeDeviceEntryFaceAuthRepository } +val Kosmos.fakeDeviceEntryFaceAuthRepository by + Kosmos.Fixture { FakeDeviceEntryFaceAuthRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt index df31a12b8415..1381464c6ad1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt @@ -47,7 +47,7 @@ class FakeBiometricSettingsRepository @Inject constructor() : BiometricSettingsR get() = _isFaceAuthCurrentlyAllowed private val _isFaceAuthSupportedInCurrentPosture = MutableStateFlow(false) - override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> + override val isFaceAuthSupportedInCurrentPosture: StateFlow<Boolean> get() = _isFaceAuthSupportedInCurrentPosture override val isCurrentUserInLockdown: Flow<Boolean> diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt new file mode 100644 index 000000000000..b0e4ba06d7b2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.inWindowLauncherUnlockAnimationRepository by + Kosmos.Fixture { InWindowLauncherUnlockAnimationRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt new file mode 100644 index 000000000000..453fef5ddca9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.keyguardRepository: KeyguardRepository by Kosmos.Fixture { fakeKeyguardRepository } +val Kosmos.fakeKeyguardRepository by Kosmos.Fixture { FakeKeyguardRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt new file mode 100644 index 000000000000..c900ac9771a7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.keyguardSurfaceBehindRepository: KeyguardSurfaceBehindRepository by + Kosmos.Fixture { fakeKeyguardSurfaceBehindRepository } +val Kosmos.fakeKeyguardSurfaceBehindRepository by + Kosmos.Fixture { FakeKeyguardSurfaceBehindRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt new file mode 100644 index 000000000000..008f79a377e0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.keyguardTransitionRepository: KeyguardTransitionRepository by + Kosmos.Fixture { fakeKeyguardTransitionRepository } +val Kosmos.fakeKeyguardTransitionRepository by Kosmos.Fixture { FakeKeyguardTransitionRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt new file mode 100644 index 000000000000..ca87acf88d01 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.trustRepository: TrustRepository by Kosmos.Fixture { fakeTrustRepository } +val Kosmos.fakeTrustRepository by Kosmos.Fixture { FakeTrustRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..b03d0b822161 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 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.keyguard.domain.interactor + +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.shade.data.repository.shadeRepository +import dagger.Lazy + +val Kosmos.fromLockscreenTransitionInteractor by + Kosmos.Fixture { + FromLockscreenTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + keyguardInteractor = keyguardInteractor, + flags = featureFlagsClassic, + shadeRepository = shadeRepository, + powerInteractor = powerInteractor, + inWindowLauncherUnlockAnimationInteractor = + Lazy { inWindowLauncherUnlockAnimationInteractor }, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..ade3e1a82297 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 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.keyguard.domain.interactor + +import com.android.keyguard.keyguardSecurityModel +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.user.domain.interactor.selectedUserInteractor + +val Kosmos.fromPrimaryBouncerTransitionInteractor by + Kosmos.Fixture { + FromPrimaryBouncerTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + keyguardInteractor = keyguardInteractor, + flags = featureFlagsClassic, + keyguardSecurityModel = keyguardSecurityModel, + selectedUserInteractor = selectedUserInteractor, + powerInteractor = powerInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt new file mode 100644 index 000000000000..dbbb203b0570 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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.keyguard.domain.interactor + +import com.android.systemui.keyguard.data.repository.inWindowLauncherUnlockAnimationRepository +import com.android.systemui.keyguard.data.repository.keyguardSurfaceBehindRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.shared.system.activityManagerWrapper +import dagger.Lazy + +val Kosmos.inWindowLauncherUnlockAnimationInteractor by + Kosmos.Fixture { + InWindowLauncherUnlockAnimationInteractor( + repository = inWindowLauncherUnlockAnimationRepository, + scope = applicationCoroutineScope, + transitionInteractor = keyguardTransitionInteractor, + surfaceBehindRepository = Lazy { keyguardSurfaceBehindRepository }, + activityManager = activityManagerWrapper, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt index 6c2ce7139375..3d8ae1e9801a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt @@ -81,6 +81,7 @@ object KeyguardDismissInteractorFactory { trustRepository, testScope.backgroundScope, mock(SelectedUserInteractor::class.java), + mock(KeyguardFaceAuthInteractor::class.java), ) val alternateBouncerInteractor = AlternateBouncerInteractor( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt index d2ff9bc5f3eb..c575bb3fe25d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.power.domain.interactor.PowerInteractor @@ -40,7 +39,7 @@ object KeyguardInteractorFactory { @JvmOverloads @JvmStatic fun create( - featureFlags: FakeFeatureFlags = createFakeFeatureFlags(), + featureFlags: FakeFeatureFlags = FakeFeatureFlags(), sceneContainerFlags: SceneContainerFlags = FakeSceneContainerFlags(), repository: FakeKeyguardRepository = FakeKeyguardRepository(), commandQueue: FakeCommandQueue = FakeCommandQueue(), @@ -62,7 +61,6 @@ object KeyguardInteractorFactory { KeyguardInteractor( repository = repository, commandQueue = commandQueue, - featureFlags = featureFlags, sceneContainerFlags = sceneContainerFlags, bouncerRepository = bouncerRepository, configurationRepository = configurationRepository, @@ -73,11 +71,6 @@ object KeyguardInteractorFactory { ) } - /** Provide defaults, otherwise tests will throw an error */ - private fun createFakeFeatureFlags(): FakeFeatureFlags { - return FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } - } - data class WithDependencies( val repository: FakeKeyguardRepository, val commandQueue: FakeCommandQueue, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt new file mode 100644 index 000000000000..bb840362185f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 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.keyguard.domain.interactor + +import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository +import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.sceneContainerFlags +import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.statusbar.commandQueue + +val Kosmos.keyguardInteractor by + Kosmos.Fixture { + KeyguardInteractor( + repository = keyguardRepository, + commandQueue = commandQueue, + powerInteractor = powerInteractor, + sceneContainerFlags = sceneContainerFlags, + bouncerRepository = keyguardBouncerRepository, + configurationRepository = configurationRepository, + shadeRepository = shadeRepository, + sceneInteractorProvider = { sceneInteractor }, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..e4d115e16b6a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.keyguard.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import dagger.Lazy + +val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by + Kosmos.Fixture { + KeyguardTransitionInteractor( + scope = applicationCoroutineScope, + repository = keyguardTransitionRepository, + keyguardInteractor = Lazy { keyguardInteractor }, + fromLockscreenTransitionInteractor = Lazy { fromLockscreenTransitionInteractor }, + fromPrimaryBouncerTransitionInteractor = + Lazy { fromPrimaryBouncerTransitionInteractor }, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt index b05915c4f678..0b1385865d63 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt @@ -1,16 +1,11 @@ package com.android.systemui.kosmos -import android.content.Context -import android.os.UserManager +import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.Kosmos.Fixture -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope -import org.mockito.Mockito var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() } var Kosmos.testScope by Fixture { TestScope(testDispatcher) } -var Kosmos.context by Fixture<Context>() -var Kosmos.lifecycleScope by Fixture<CoroutineScope>() - -val Kosmos.userManager by Fixture { Mockito.mock(UserManager::class.java) } +var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope } +var Kosmos.testCase: SysuiTestCase by Fixture() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt new file mode 100644 index 000000000000..0ec8d49ec29b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.plugins + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.activityStarter by Kosmos.Fixture { mock<ActivityStarter>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt new file mode 100644 index 000000000000..cac2646a58f2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.plugins.statusbar + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.statusBarStateController by Kosmos.Fixture { mock<StatusBarStateController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt new file mode 100644 index 000000000000..c924579d43e5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.power.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.powerRepository: PowerRepository by Kosmos.Fixture { fakePowerRepository } +val Kosmos.fakePowerRepository by Kosmos.Fixture { FakePowerRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt new file mode 100644 index 000000000000..8486691a7b95 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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.power.domain.interactor + +import com.android.systemui.classifier.falsingCollector +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.plugins.statusbar.statusBarStateController +import com.android.systemui.power.data.repository.powerRepository +import com.android.systemui.statusbar.phone.screenOffAnimationController + +val Kosmos.powerInteractor by + Kosmos.Fixture { + PowerInteractor( + repository = powerRepository, + falsingCollector = falsingCollector, + screenOffAnimationController = screenOffAnimationController, + statusBarStateController = statusBarStateController, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 80c38b2d228c..3c96051a718f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -120,7 +120,6 @@ class SceneTestUtils( val testScope = kosmos.testScope val featureFlags = FakeFeatureFlagsClassic().apply { - set(Flags.FACE_AUTH_REFACTOR, false) set(Flags.FULL_SCREEN_USER_SWITCHER, false) set(Flags.NSSL_DEBUG_LINES, false) } @@ -245,7 +244,6 @@ class SceneTestUtils( return KeyguardInteractor( repository = repository, commandQueue = FakeCommandQueue(), - featureFlags = featureFlags, sceneContainerFlags = sceneContainerFlags, bouncerRepository = FakeKeyguardBouncerRepository(), configurationRepository = configurationRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt new file mode 100644 index 000000000000..7c4e160f6d05 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.scene.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.shared.model.sceneContainerConfig + +val Kosmos.sceneContainerRepository by + Kosmos.Fixture { SceneContainerRepository(applicationCoroutineScope, sceneContainerConfig) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt new file mode 100644 index 000000000000..998987602234 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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.scene.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.shared.logger.sceneLogger + +val Kosmos.sceneInteractor by + Kosmos.Fixture { + SceneInteractor( + applicationScope = applicationCoroutineScope, + repository = sceneContainerRepository, + powerInteractor = powerInteractor, + logger = sceneLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt new file mode 100644 index 000000000000..a3ceef021c59 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.scene.shared.flag + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.sceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt new file mode 100644 index 000000000000..c5f24f4f049d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.scene.shared.logger + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.sceneLogger by Kosmos.Fixture { mock<SceneLogger>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt new file mode 100644 index 000000000000..f9cdc1bea309 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.scene.shared.model + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.sceneContainerConfig by + Kosmos.Fixture { FakeSceneContainerConfigModule().sceneContainerConfig } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt new file mode 100644 index 000000000000..38cedbca3886 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.shadeRepository: ShadeRepository by Kosmos.Fixture { fakeShadeRepository } +val Kosmos.fakeShadeRepository by Kosmos.Fixture { FakeShadeRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt new file mode 100644 index 000000000000..7da57f024ec7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 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.domain.interactor + +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.sceneContainerFlags +import com.android.systemui.shade.ShadeModule +import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository +import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.dozeParameters +import com.android.systemui.statusbar.pipeline.mobile.data.repository.userSetupRepository +import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository +import com.android.systemui.user.domain.interactor.userSwitcherInteractor + +var Kosmos.baseShadeInteractor: BaseShadeInteractor by + Kosmos.Fixture { + ShadeModule.provideBaseShadeInteractor( + sceneContainerFlags = sceneContainerFlags, + sceneContainerOn = { shadeInteractorSceneContainerImpl }, + sceneContainerOff = { shadeInteractorLegacyImpl }, + ) + } +val Kosmos.shadeInteractorSceneContainerImpl by + Kosmos.Fixture { + ShadeInteractorSceneContainerImpl( + scope = applicationCoroutineScope, + sceneInteractor = sceneInteractor, + sharedNotificationContainerInteractor = sharedNotificationContainerInteractor, + ) + } +val Kosmos.shadeInteractorLegacyImpl by + Kosmos.Fixture { + ShadeInteractorLegacyImpl( + scope = applicationCoroutineScope, + keyguardRepository = keyguardRepository, + sharedNotificationContainerInteractor = sharedNotificationContainerInteractor, + repository = shadeRepository + ) + } +var Kosmos.shadeInteractor: ShadeInteractor by Kosmos.Fixture { shadeInteractorImpl } +val Kosmos.shadeInteractorImpl by + Kosmos.Fixture { + ShadeInteractorImpl( + scope = applicationCoroutineScope, + deviceProvisioningRepository = deviceProvisioningRepository, + disableFlagsRepository = disableFlagsRepository, + dozeParams = dozeParameters, + keyguardRepository = fakeKeyguardRepository, + keyguardTransitionInteractor = keyguardTransitionInteractor, + powerInteractor = powerInteractor, + userSetupRepository = userSetupRepository, + userSwitcherInteractor = userSwitcherInteractor, + baseShadeInteractor = baseShadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt new file mode 100644 index 000000000000..e75359386b2f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.shared.system + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.activityManagerWrapper by Kosmos.Fixture { mock<ActivityManagerWrapper>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt new file mode 100644 index 000000000000..27f7f6823cc7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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 + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.commandQueue by Kosmos.Fixture { mock<CommandQueue>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt index f25d282208f0..60690838fcdf 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt @@ -16,14 +16,33 @@ package com.android.systemui.statusbar.data.repository +import android.view.Display +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.data.model.StatusBarAppearance import com.android.systemui.statusbar.data.model.StatusBarMode +import com.google.common.truth.Truth.assertThat import dagger.Binds import dagger.Module import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow -class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepository { +@SysUISingleton +class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositoryStore { + + companion object { + const val DISPLAY_ID = Display.DEFAULT_DISPLAY + } + + override val defaultDisplay: FakeStatusBarModePerDisplayRepository = + FakeStatusBarModePerDisplayRepository() + + override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository { + assertThat(displayId).isEqualTo(DISPLAY_ID) + return defaultDisplay + } +} + +class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository { override val isTransientShown = MutableStateFlow(false) override val isInFullscreenMode = MutableStateFlow(false) override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null) @@ -39,5 +58,5 @@ class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositor @Module interface FakeStatusBarModeRepositoryModule { - @Binds fun bindFake(fake: FakeStatusBarModeRepository): StatusBarModeRepository + @Binds fun bindFake(fake: FakeStatusBarModeRepository): StatusBarModeRepositoryStore } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt new file mode 100644 index 000000000000..10151ac92c35 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.notificationListenerSettingsRepository by + Kosmos.Fixture { NotificationListenerSettingsRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt new file mode 100644 index 000000000000..a373a8e1844d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.disableflags.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.disableFlagsRepository: DisableFlagsRepository by + Kosmos.Fixture { fakeDisableFlagsRepository } +val Kosmos.fakeDisableFlagsRepository by Kosmos.Fixture { FakeDisableFlagsRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt new file mode 100644 index 000000000000..5507d6c2c90d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.activeNotificationListRepository by Kosmos.Fixture { ActiveNotificationListRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt new file mode 100644 index 000000000000..ed62fda6fc99 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.headsUpNotificationIconViewStateRepository by + Kosmos.Fixture { HeadsUpNotificationIconViewStateRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt new file mode 100644 index 000000000000..f2b9da413c22 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.notificationsKeyguardViewStateRepository: NotificationsKeyguardViewStateRepository by + Kosmos.Fixture { fakeNotificationsKeyguardViewStateRepository } +val Kosmos.fakeNotificationsKeyguardViewStateRepository by + Kosmos.Fixture { FakeNotificationsKeyguardViewStateRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt new file mode 100644 index 000000000000..3d7fb6d91393 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository + +val Kosmos.activeNotificationsInteractor by + Kosmos.Fixture { ActiveNotificationsInteractor(activeNotificationListRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt new file mode 100644 index 000000000000..d14c8548f3cf --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.data.repository.headsUpNotificationIconViewStateRepository + +val Kosmos.headsUpNotificationIconInteractor by + Kosmos.Fixture { HeadsUpNotificationIconInteractor(headsUpNotificationIconViewStateRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt new file mode 100644 index 000000000000..e7bd5ea2b174 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.icon.domain.interactor + +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.data.repository.notificationListenerSettingsRepository +import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.wm.shell.bubbles.bubblesOptional +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.alwaysOnDisplayNotificationIconsInteractor by + Kosmos.Fixture { + AlwaysOnDisplayNotificationIconsInteractor( + deviceEntryInteractor = deviceEntryInteractor, + iconsInteractor = notificationIconsInteractor, + ) + } +val Kosmos.statusBarNotificationIconsInteractor by + Kosmos.Fixture { + StatusBarNotificationIconsInteractor( + iconsInteractor = notificationIconsInteractor, + settingsRepository = notificationListenerSettingsRepository, + ) + } +val Kosmos.notificationIconsInteractor by + Kosmos.Fixture { + NotificationIconsInteractor( + activeNotificationsInteractor = activeNotificationsInteractor, + bubbles = bubblesOptional, + keyguardViewStateRepository = notificationsKeyguardViewStateRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt new file mode 100644 index 000000000000..6295b83a2bd0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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.icon.ui.viewmodel + +import android.content.res.mainResources +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.icon.domain.interactor.alwaysOnDisplayNotificationIconsInteractor + +val Kosmos.notificationIconContainerAlwaysOnDisplayViewModel by + Kosmos.Fixture { + NotificationIconContainerAlwaysOnDisplayViewModel( + iconsInteractor = alwaysOnDisplayNotificationIconsInteractor, + keyguardInteractor = keyguardInteractor, + keyguardTransitionInteractor = keyguardTransitionInteractor, + resources = mainResources, + shadeInteractor = shadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt new file mode 100644 index 000000000000..04bb52d101db --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 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.icon.ui.viewmodel + +import android.content.res.mainResources +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor +import com.android.systemui.statusbar.notification.icon.domain.interactor.statusBarNotificationIconsInteractor +import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor + +val Kosmos.notificationIconContainerStatusBarViewModel by + Kosmos.Fixture { + NotificationIconContainerStatusBarViewModel( + darkIconInteractor = darkIconInteractor, + iconsInteractor = statusBarNotificationIconsInteractor, + headsUpIconInteractor = headsUpNotificationIconInteractor, + keyguardInteractor = keyguardInteractor, + notificationsInteractor = activeNotificationsInteractor, + resources = mainResources, + shadeInteractor = shadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt new file mode 100644 index 000000000000..3403227f6d27 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 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.domain.interactor + +import android.content.applicationContext +import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.splitShadeStateController + +val Kosmos.sharedNotificationContainerInteractor by + Kosmos.Fixture { + SharedNotificationContainerInteractor( + configurationRepository = configurationRepository, + context = applicationContext, + splitShadeStateController = splitShadeStateController, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt new file mode 100644 index 000000000000..9f6b181f8348 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.phone + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.dozeParameters by Kosmos.Fixture { mock<DozeParameters>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt new file mode 100644 index 000000000000..d4c21f66d394 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.phone + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.screenOffAnimationController by Kosmos.Fixture { mock<ScreenOffAnimationController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt new file mode 100644 index 000000000000..977dcb738ba1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.phone.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.darkIconRepository: DarkIconRepository by Kosmos.Fixture { fakeDarkIconRepository } +val Kosmos.fakeDarkIconRepository by Kosmos.Fixture { FakeDarkIconRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt new file mode 100644 index 000000000000..db678d487b3d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.phone.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.phone.data.repository.darkIconRepository + +val Kosmos.darkIconInteractor by Kosmos.Fixture { DarkIconInteractor(darkIconRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt new file mode 100644 index 000000000000..7b9634a7abb5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.pipeline.mobile.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.userSetupRepository: UserSetupRepository by Kosmos.Fixture { fakeUserSetupRepository } +val Kosmos.fakeUserSetupRepository by Kosmos.Fixture { FakeUserSetupRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt new file mode 100644 index 000000000000..18a2f9482df8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.policy + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.configurationController by Kosmos.Fixture { mock<ConfigurationController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt new file mode 100644 index 000000000000..6a77c882eaa1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.policy + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceProvisionedController: DeviceProvisionedController by + Kosmos.Fixture { fakeDeviceProvisionedController } +val Kosmos.fakeDeviceProvisionedController by Kosmos.Fixture { FakeDeviceProvisionedController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt new file mode 100644 index 000000000000..5e430381e293 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.policy + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.splitShadeStateController: SplitShadeStateController by + Kosmos.Fixture { resourcesSplitShadeStateController } +val Kosmos.resourcesSplitShadeStateController by + Kosmos.Fixture { ResourcesSplitShadeStateController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt new file mode 100644 index 000000000000..56a0e02e41dc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.policy.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceProvisioningRepository: DeviceProvisioningRepository by + Kosmos.Fixture { fakeDeviceProvisioningRepository } +val Kosmos.fakeDeviceProvisioningRepository by Kosmos.Fixture { FakeDeviceProvisioningRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt new file mode 100644 index 000000000000..6bb5ec5f0313 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.telephony.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.telephonyRepository: TelephonyRepository by Kosmos.Fixture { fakeTelephonyRepository } +val Kosmos.fakeTelephonyRepository by Kosmos.Fixture { FakeTelephonyRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt new file mode 100644 index 000000000000..02ca96e3521b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.telephony.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.telephony.data.repository.telephonyRepository + +val Kosmos.telephonyInteractor by Kosmos.Fixture { TelephonyInteractor(telephonyRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt index 8bce9b6d461d..9bb52623bdcd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt @@ -18,4 +18,5 @@ package com.android.systemui.user.data.repository import com.android.systemui.kosmos.Kosmos -val Kosmos.userRepository by Kosmos.Fixture { FakeUserRepository() } +var Kosmos.userRepository: UserRepository by Kosmos.Fixture { fakeUserRepository } +val Kosmos.fakeUserRepository by Kosmos.Fixture { FakeUserRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt index e69570433d43..3b1c3f0198c7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt @@ -16,28 +16,32 @@ package com.android.systemui.user.domain.interactor +import android.app.admin.devicePolicyManager +import android.content.applicationContext +import android.os.userManager +import com.android.internal.logging.uiEventLogger +import com.android.systemui.guestResetOrExitSessionReceiver +import com.android.systemui.guestResumeSessionReceiver import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.context -import com.android.systemui.kosmos.lifecycleScope +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.userManager +import com.android.systemui.statusbar.policy.deviceProvisionedController import com.android.systemui.user.data.repository.userRepository -import com.android.systemui.util.mockito.mock val Kosmos.guestUserInteractor by Kosmos.Fixture { GuestUserInteractor( - applicationContext = context, - applicationScope = lifecycleScope, + applicationContext = applicationContext, + applicationScope = applicationCoroutineScope, mainDispatcher = testDispatcher, backgroundDispatcher = testDispatcher, manager = userManager, - deviceProvisionedController = mock(), repository = userRepository, - devicePolicyManager = mock(), + deviceProvisionedController = deviceProvisionedController, + devicePolicyManager = devicePolicyManager, refreshUsersScheduler = refreshUsersScheduler, - uiEventLogger = mock(), - resumeSessionReceiver = mock(), - resetOrExitSessionReceiver = mock(), + uiEventLogger = uiEventLogger, + resumeSessionReceiver = guestResumeSessionReceiver, + resetOrExitSessionReceiver = guestResetOrExitSessionReceiver, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt new file mode 100644 index 000000000000..de9f69bc9215 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.user.domain.interactor + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.headlessSystemUserMode by Kosmos.Fixture { HeadlessSystemUserModeImpl() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt index 87a2fe0249e3..14da8b0f461c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt @@ -17,15 +17,11 @@ package com.android.systemui.user.domain.interactor import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.lifecycleScope +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.user.data.repository.userRepository val Kosmos.refreshUsersScheduler by Kosmos.Fixture { - RefreshUsersScheduler( - applicationScope = lifecycleScope, - mainDispatcher = testDispatcher, - repository = userRepository, - ) + RefreshUsersScheduler(applicationCoroutineScope, testDispatcher, userRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt new file mode 100644 index 000000000000..427f92a7f514 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.user.domain.interactor + +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.user.data.repository.userRepository + +val Kosmos.selectedUserInteractor by + Kosmos.Fixture { SelectedUserInteractor(userRepository, featureFlagsClassic) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt index 6d6b2683a7ea..42c77aaac53f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt @@ -16,42 +16,41 @@ package com.android.systemui.user.domain.interactor +import android.app.activityManager +import android.content.applicationContext +import android.os.userManager +import com.android.internal.logging.uiEventLogger +import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.broadcast.broadcastDispatcher -import com.android.systemui.flags.featureFlags -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.context -import com.android.systemui.kosmos.lifecycleScope +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.userManager -import com.android.systemui.telephony.data.repository.FakeTelephonyRepository -import com.android.systemui.telephony.domain.interactor.TelephonyInteractor +import com.android.systemui.plugins.activityStarter +import com.android.systemui.telephony.domain.interactor.telephonyInteractor import com.android.systemui.user.data.repository.userRepository -import com.android.systemui.util.mockito.mock +import com.android.systemui.utils.userRestrictionChecker val Kosmos.userSwitcherInteractor by Kosmos.Fixture { UserSwitcherInteractor( - applicationContext = context, + applicationContext = applicationContext, repository = userRepository, - activityStarter = mock(), - keyguardInteractor = - KeyguardInteractorFactory.create(featureFlags = featureFlags).keyguardInteractor, - featureFlags = featureFlags, + activityStarter = activityStarter, + keyguardInteractor = keyguardInteractor, + featureFlags = featureFlagsClassic, manager = userManager, - headlessSystemUserMode = mock(), - applicationScope = lifecycleScope, - telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), + headlessSystemUserMode = headlessSystemUserMode, + applicationScope = applicationCoroutineScope, + telephonyInteractor = telephonyInteractor, broadcastDispatcher = broadcastDispatcher, - keyguardUpdateMonitor = mock(), + keyguardUpdateMonitor = keyguardUpdateMonitor, backgroundDispatcher = testDispatcher, - activityManager = mock(), + activityManager = activityManager, refreshUsersScheduler = refreshUsersScheduler, guestUserInteractor = guestUserInteractor, - uiEventLogger = mock(), - userRestrictionChecker = mock() + uiEventLogger = uiEventLogger, + userRestrictionChecker = userRestrictionChecker, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt new file mode 100644 index 000000000000..914e65427f41 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.time + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.fakeSystemClock by Kosmos.Fixture { FakeSystemClock() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt new file mode 100644 index 000000000000..24d5d2f72289 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.utils + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.userRestrictionChecker by Kosmos.Fixture { UserRestrictionChecker() } diff --git a/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt new file mode 100644 index 000000000000..a7a37b22c969 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.wm.shell.bubbles + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock +import java.util.Optional + +var Kosmos.bubblesOptional by Kosmos.Fixture { Optional.of(bubbles) } +var Kosmos.bubbles by Kosmos.Fixture { mock<Bubbles> {} } diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java index 1d315798d647..f02f06c056bd 100644 --- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java +++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java @@ -18,7 +18,6 @@ package android.ravenwood.annotation; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,7 +31,7 @@ import java.lang.annotation.Target; * * @hide */ -@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Target({FIELD, METHOD, CONSTRUCTOR}) @Retention(RetentionPolicy.CLASS) public @interface RavenwoodKeep { } diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java new file mode 100644 index 000000000000..784727410188 --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.ravenwood.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * TODO: Javadoc + * + * @hide + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodKeepPartialClass { +} diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java new file mode 100644 index 000000000000..eeebee985e4a --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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.ravenwood.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * @hide + */ +@Target(TYPE) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodKeepStaticInitializer { +} diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt index 28639d4960a4..48e93280f73f 100644 --- a/ravenwood/framework-minus-apex-ravenwood-policies.txt +++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt @@ -17,12 +17,14 @@ class android.util.MapCollections stubclass class android.util.Log stubclass class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host class android.util.LogPrinter stubclass +class android.util.LocalLog stubclass # String Manipulation class android.util.Printer stubclass class android.util.PrintStreamPrinter stubclass class android.util.PrintWriterPrinter stubclass class android.util.StringBuilderPrinter stubclass +class android.util.IndentingPrintWriter stubclass # Properties class android.util.Property stubclass @@ -76,6 +78,7 @@ class android.util.Patterns stubclass class android.util.UtilConfig stubclass # Internals +class com.android.internal.util.FastPrintWriter stubclass class com.android.internal.util.GrowingArrayUtils stubclass class com.android.internal.util.LineBreakBufferedWriter stubclass class com.android.internal.util.Preconditions stubclass diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index df44fde2ed72..a791f682fecf 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -2,6 +2,12 @@ com.android.internal.util.ArrayUtils +android.util.DataUnit +android.util.EventLog +android.util.IntArray +android.util.LongArray +android.util.Slog +android.util.TimeUtils android.util.Xml android.os.Binder diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt index 4b07ef6e35a8..f842f33bc95b 100644 --- a/ravenwood/ravenwood-standard-options.txt +++ b/ravenwood/ravenwood-standard-options.txt @@ -18,6 +18,9 @@ --keep-annotation android.ravenwood.annotation.RavenwoodKeep +--keep-annotation + android.ravenwood.annotation.RavenwoodKeepPartialClass + --keep-class-annotation android.ravenwood.annotation.RavenwoodKeepWholeClass @@ -35,3 +38,6 @@ --class-load-hook-annotation android.ravenwood.annotation.RavenwoodClassLoadHook + +--keep-static-initializer-annotation + android.ravenwood.annotation.RavenwoodKeepStaticInitializer diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 5635dd59e3f1..42ab05fdd231 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -21,6 +21,7 @@ import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityOptions; import android.app.Dialog; import android.app.PendingIntent; import android.content.ComponentName; @@ -205,7 +206,10 @@ final class SaveUi { intent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT, - /* options= */ null, UserHandle.CURRENT); + ActivityOptions.makeBasic() + .setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) + .toBundle(), UserHandle.CURRENT); if (sDebug) { Slog.d(TAG, "startActivity add save UI restored with intent=" + intent); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index b9c269c91651..71a1f012e5cb 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -75,7 +75,6 @@ import android.companion.IOnMessageReceivedListener; import android.companion.IOnTransportsChangedListener; import android.companion.ISystemDataTransferCallback; import android.companion.datatransfer.PermissionSyncRequest; -import android.companion.utils.FeatureUtils; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; @@ -829,11 +828,6 @@ public class CompanionDeviceManagerService extends SystemService { @Override public PendingIntent buildPermissionTransferUserConsentIntent(String packageName, int userId, int associationId) { - if (!FeatureUtils.isPermSyncEnabled()) { - throw new UnsupportedOperationException("Calling" - + " buildPermissionTransferUserConsentIntent, but this API is disabled by" - + " the system."); - } return mSystemDataTransferProcessor.buildPermissionTransferUserConsentIntent( packageName, userId, associationId); } @@ -841,10 +835,6 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void startSystemDataTransfer(String packageName, int userId, int associationId, ISystemDataTransferCallback callback) { - if (!FeatureUtils.isPermSyncEnabled()) { - throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this" - + " API is disabled by the system."); - } mSystemDataTransferProcessor.startSystemDataTransfer(packageName, userId, associationId, callback); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index 1f6261383961..23e7ce68c1d0 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -20,6 +20,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_S import android.companion.AssociationInfo; import android.companion.ContextSyncMessage; +import android.companion.Flags; import android.companion.Telecom; import android.companion.datatransfer.PermissionSyncRequest; import android.net.MacAddress; @@ -65,7 +66,14 @@ class CompanionDeviceShellCommand extends ShellCommand { public int onCommand(String cmd) { final PrintWriter out = getOutPrintWriter(); final int associationId; + try { + if ("simulate-device-event".equals(cmd) && Flags.devicePresence()) { + associationId = getNextIntArgRequired(); + int event = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceEvent(associationId, event); + return 0; + } switch (cmd) { case "list": { final int userId = getNextIntArgRequired(); @@ -107,10 +115,15 @@ class CompanionDeviceShellCommand extends ShellCommand { mService.loadAssociationsFromDisk(); break; - case "simulate-device-event": + case "simulate-device-appeared": + associationId = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 0); + break; + + case "simulate-device-disappeared": associationId = getNextIntArgRequired(); - int event = getNextIntArgRequired(); - mDevicePresenceMonitor.simulateDeviceEvent(associationId, event); + mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 1); + break; case "remove-inactive-associations": { // This command should trigger the same "clean-up" job as performed by the @@ -346,9 +359,7 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY."); pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); - pw.println(" simulate-device-event ASSOCIATION_ID EVENT"); - pw.println(" Simulate the companion device event changes:"); - pw.println(" Case(0): "); + pw.println(" simulate-device-appeared ASSOCIATION_ID"); pw.println(" Make CDM act as if the given companion device has appeared."); pw.println(" I.e. bind the associated companion application's"); pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback."); @@ -356,18 +367,43 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" will act as if device disappeared, unless 'simulate-device-disappeared'"); pw.println(" or 'simulate-device-appeared' is called again before 60 seconds run out" + "."); - pw.println(" Case(1): "); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" simulate-device-disappeared ASSOCIATION_ID"); pw.println(" Make CDM act as if the given companion device has disappeared."); pw.println(" I.e. unbind the associated companion application's"); pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared() callback."); pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was"); pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than"); pw.println(" 60 seconds ago."); - pw.println(" Case(2): "); - pw.println(" Make CDM act as if the given companion device is BT connected "); - pw.println(" Case(3): "); - pw.println(" Make CDM act as if the given companion device is BT disconnected "); - pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + if (Flags.devicePresence()) { + pw.println(" simulate-device-event ASSOCIATION_ID EVENT"); + pw.println(" Simulate the companion device event changes:"); + pw.println(" Case(0): "); + pw.println(" Make CDM act as if the given companion device has appeared."); + pw.println(" I.e. bind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback."); + pw.println(" The CDM will consider the devices as present for" + + "60 seconds and then"); + pw.println(" will act as if device disappeared, unless" + + "'simulate-device-disappeared'"); + pw.println(" or 'simulate-device-appeared' is called again before 60 seconds" + + "run out."); + pw.println(" Case(1): "); + pw.println(" Make CDM act as if the given companion device has disappeared."); + pw.println(" I.e. unbind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared()" + + "callback."); + pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was"); + pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than"); + pw.println(" 60 seconds ago."); + pw.println(" Case(2): "); + pw.println(" Make CDM act as if the given companion device is BT connected "); + pw.println(" Case(3): "); + pw.println(" Make CDM act as if the given companion device is BT disconnected "); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + } pw.println(" remove-inactive-associations"); pw.println(" Remove self-managed associations that have not been active "); diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java index 8fea078c3183..e42b9356cca3 100644 --- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -79,7 +79,7 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange void onDeviceDisappeared(int associationId); /**Invoked when device has corresponding event changes. */ - void onDeviceEvent(int associationId, int state); + void onDeviceEvent(int associationId, int event); } private final @NonNull AssociationStore mAssociationStore; diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 215970eedff1..e51ef297519f 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -864,6 +864,23 @@ public class VirtualDeviceManagerService extends SystemService { } @Override + public @NonNull Set<String> getAllPersistentDeviceIds() { + Set<String> persistentIds = new ArraySet<>(); + synchronized (mVirtualDeviceManagerLock) { + for (int i = 0; i < mActiveAssociations.size(); ++i) { + AssociationInfo associationInfo = mActiveAssociations.get(i); + if (VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains( + associationInfo.getDeviceProfile())) { + persistentIds.add( + VirtualDeviceImpl.createPersistentDeviceId( + associationInfo.getId())); + } + } + } + return persistentIds; + } + + @Override public void registerAppsOnVirtualDeviceListener( @NonNull AppsOnVirtualDeviceListener listener) { synchronized (mVirtualDeviceManagerLock) { diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java index a570d0989134..6940ffe40a51 100644 --- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java +++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java @@ -69,6 +69,11 @@ public final class VirtualCameraConversionUtil { } @Override + public void onProcessCaptureRequest(int streamId, int frameId) throws RemoteException { + camera.onProcessCaptureRequest(streamId, frameId, /*metadata=*/ null); + } + + @Override public void onStreamClosed(int streamId) throws RemoteException { camera.onStreamClosed(streamId); } diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 7907d616d1b5..77b6d583808c 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -1182,8 +1182,8 @@ public class BinaryTransparencyService extends SystemService { // we are only interested in doing things at PHASE_BOOT_COMPLETED if (phase == PHASE_BOOT_COMPLETED) { - Slog.i(TAG, "Boot completed. Getting VBMeta Digest."); - getVBMetaDigestInformation(); + Slog.i(TAG, "Boot completed. Getting boot integrity data."); + collectBootIntegrityInfo(); // Log to statsd // TODO(b/264061957): For now, biometric system properties are always collected if users @@ -1458,10 +1458,19 @@ public class BinaryTransparencyService extends SystemService { } } - private void getVBMetaDigestInformation() { + private void collectBootIntegrityInfo() { mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE); Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest)); FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest); + + if (android.security.Flags.binaryTransparencySepolicyHash()) { + byte[] sepolicyHash = PackageUtils.computeSha256DigestForLargeFileAsBytes( + "/sys/fs/selinux/policy", PackageUtils.createLargeFileBuffer()); + String sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false); + Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded); + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_INTEGRITY_INFO_REPORTED, + sepolicyHashEncoded, mVbmetaDigest); + } } /** diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 8cd5ce1f4ff8..ce1a8756a849 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -158,6 +158,10 @@ public final class PinnerService extends SystemService { @GuardedBy("this") private ArraySet<Integer> mPinKeys; + // Note that we don't use the `_BOOT` namespace for anonymous pinnings, as we want + // them to be responsive to dynamic flag changes for experimentation. + private static final String DEVICE_CONFIG_NAMESPACE_ANON_SIZE = + DeviceConfig.NAMESPACE_RUNTIME_NATIVE; private static final String DEVICE_CONFIG_KEY_ANON_SIZE = "pin_shared_anon_size"; private static final long DEFAULT_ANON_SIZE = SystemProperties.getLong("pinner.pin_shared_anon_size", 0); @@ -188,11 +192,11 @@ public final class PinnerService extends SystemService { } }; - private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener = + private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigAnonSizeListener = new DeviceConfig.OnPropertiesChangedListener() { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { - if (DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT.equals(properties.getNamespace()) + if (DEVICE_CONFIG_NAMESPACE_ANON_SIZE.equals(properties.getNamespace()) && properties.getKeyset().contains(DEVICE_CONFIG_KEY_ANON_SIZE)) { refreshPinAnonConfig(); } @@ -246,9 +250,9 @@ public final class PinnerService extends SystemService { registerUserSetupCompleteListener(); mDeviceConfigInterface.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + DEVICE_CONFIG_NAMESPACE_ANON_SIZE, new HandlerExecutor(mPinnerHandler), - mDeviceConfigListener); + mDeviceConfigAnonSizeListener); } @Override @@ -733,7 +737,7 @@ public final class PinnerService extends SystemService { private void refreshPinAnonConfig() { long newPinAnonSize = mDeviceConfigInterface.getLong( - DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + DEVICE_CONFIG_NAMESPACE_ANON_SIZE, DEVICE_CONFIG_KEY_ANON_SIZE, DEFAULT_ANON_SIZE); newPinAnonSize = Math.max(0, Math.min(newPinAnonSize, MAX_ANON_SIZE)); diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 5b54561c2164..e07631cfbdb0 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -193,6 +193,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private @Nullable BroadcastProcessQueue mRunningColdStart; /** + * Indicates whether we have queued a message to check pending cold start validity. + */ + @GuardedBy("mService") + private boolean mCheckPendingColdStartQueued; + + /** * Collection of latches waiting for device to reach specific state. The * first argument is a function to test for the desired state, and the * second argument is the latch to release once that state is reached. @@ -302,7 +308,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue { return true; } case MSG_CHECK_PENDING_COLD_START_VALIDITY: { - checkPendingColdStartValidity(); + synchronized (mService) { + /* Clear this as we have just received the broadcast. */ + mCheckPendingColdStartQueued = false; + checkPendingColdStartValidityLocked(); + } return true; } case MSG_PROCESS_FREEZABLE_CHANGED: { @@ -549,7 +559,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER); } - checkPendingColdStartValidity(); + checkPendingColdStartValidityLocked(); checkAndRemoveWaitingFor(); traceEnd(cookie); @@ -573,22 +583,24 @@ class BroadcastQueueModernImpl extends BroadcastQueue { enqueueUpdateRunningList(); } - private void checkPendingColdStartValidity() { + @GuardedBy("mService") + private void checkPendingColdStartValidityLocked() { // There are a few cases where a starting process gets killed but AMS doesn't report // this event. So, once we start waiting for a pending cold start, periodically check // if the pending start is still valid and if not, clear it so that the queue doesn't // keep waiting for the process start forever. - synchronized (mService) { - // If there is no pending cold start, then nothing to do. - if (mRunningColdStart == null) { - return; - } - if (isPendingColdStartValid()) { + // If there is no pending cold start, then nothing to do. + if (mRunningColdStart == null) { + return; + } + if (isPendingColdStartValid()) { + if (!mCheckPendingColdStartQueued) { mLocalHandler.sendEmptyMessageDelayed(MSG_CHECK_PENDING_COLD_START_VALIDITY, mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS); - } else { - clearInvalidPendingColdStart(); + mCheckPendingColdStartQueued = true; } + } else { + clearInvalidPendingColdStart(); } } diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index b852ef56fceb..d372108e0a47 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -67,10 +67,8 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal.OomAdjReason; import android.content.pm.ServiceInfo; -import android.os.IBinder; import android.os.SystemClock; import android.os.Trace; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -80,7 +78,6 @@ import com.android.server.ServiceThread; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.function.Consumer; @@ -504,6 +501,28 @@ public class OomAdjusterModernImpl extends OomAdjuster { } } + /** + * A helper consumer for collecting processes that have not been reached yet. To avoid object + * allocations every OomAdjuster update, the results will be stored in + * {@link UnreachedProcessCollector#processList}. The process list reader is responsible + * for setting it before usage, as well as, clearing the reachable state of each process in the + * list. + */ + private static class UnreachedProcessCollector implements Consumer<ProcessRecord> { + public ArrayList<ProcessRecord> processList = null; + @Override + public void accept(ProcessRecord process) { + if (process.mState.isReachable()) { + return; + } + process.mState.setReachable(true); + processList.add(process); + } + } + + private final UnreachedProcessCollector mUnreachedProcessCollector = + new UnreachedProcessCollector(); + OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { this(service, processList, activeUids, createAdjusterThread()); @@ -755,23 +774,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { // We'll need to collect the upstream processes of the target apps here, because those // processes would potentially impact the procstate/adj via bindings. if (!fullUpdate) { - final boolean containsCycle = collectReversedReachableProcessesLocked(targetProcesses, - clientProcesses); - - // If any of its upstream processes are in a cycle, - // move them into the candidate targets. - if (containsCycle) { - // Add all client apps to the target process list. - for (int i = 0, size = clientProcesses.size(); i < size; i++) { - final ProcessRecord client = clientProcesses.get(i); - final UidRecord uidRec = client.getUidRecord(); - targetProcesses.add(client); - if (uidRec != null) { - uids.put(uidRec.getUid(), uidRec); - } - } - clientProcesses.clear(); - } + collectExcludedClientProcessesLocked(targetProcesses, clientProcesses); + for (int i = 0, size = targetProcesses.size(); i < size; i++) { final ProcessRecord app = targetProcesses.valueAt(i); app.mState.resetCachedInfo(); @@ -807,102 +811,36 @@ public class OomAdjusterModernImpl extends OomAdjuster { } /** - * Collect the reversed reachable processes from the given {@code apps}, the result will be - * returned in the given {@code processes}, which will <em>NOT</em> include the processes from - * the given {@code apps}. + * Collect the client processes from the given {@code apps}, the result will be returned in the + * given {@code clientProcesses}, which will <em>NOT</em> include the processes from the given + * {@code apps}. */ @GuardedBy("mService") - private boolean collectReversedReachableProcessesLocked(ArraySet<ProcessRecord> apps, + private void collectExcludedClientProcessesLocked(ArraySet<ProcessRecord> apps, ArrayList<ProcessRecord> clientProcesses) { - final ArrayDeque<ProcessRecord> queue = mTmpQueue; - queue.clear(); - clientProcesses.clear(); - for (int i = 0, size = apps.size(); i < size; i++) { + // Mark all of the provided apps as reachable to avoid including them in the client list. + final int appsSize = apps.size(); + for (int i = 0; i < appsSize; i++) { final ProcessRecord app = apps.valueAt(i); app.mState.setReachable(true); - app.mState.setReversedReachable(true); - queue.offer(app); } - // Track if any of them reachables could include a cycle - boolean containsCycle = false; - - // Scan upstreams of the process record - for (ProcessRecord pr = queue.poll(); pr != null; pr = queue.poll()) { - if (!pr.mState.isReachable()) { - // If not in the given initial set of apps, add it. - clientProcesses.add(pr); - } - final ProcessServiceRecord psr = pr.mServices; - for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) { - final ServiceRecord s = psr.getRunningServiceAt(i); - final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = - s.getConnections(); - for (int j = serviceConnections.size() - 1; j >= 0; j--) { - final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j); - for (int k = clist.size() - 1; k >= 0; k--) { - final ConnectionRecord cr = clist.get(k); - final ProcessRecord client = cr.binding.client; - containsCycle |= client.mState.isReversedReachable(); - if (client.mState.isReversedReachable()) { - continue; - } - queue.offer(client); - client.mState.setReversedReachable(true); - } - } - } - final ProcessProviderRecord ppr = pr.mProviders; - for (int i = ppr.numberOfProviders() - 1; i >= 0; i--) { - final ContentProviderRecord cpr = ppr.getProviderAt(i); - for (int j = cpr.connections.size() - 1; j >= 0; j--) { - final ContentProviderConnection conn = cpr.connections.get(j); - final ProcessRecord client = conn.client; - containsCycle |= client.mState.isReversedReachable(); - if (client.mState.isReversedReachable()) { - continue; - } - queue.offer(client); - client.mState.setReversedReachable(true); - } - } - // If this process is a sandbox itself, also add the app on whose behalf - // its running - if (pr.isSdkSandbox) { - for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) { - ServiceRecord s = psr.getRunningServiceAt(is); - ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = - s.getConnections(); - for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) { - ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni); - for (int i = clist.size() - 1; i >= 0; i--) { - ConnectionRecord cr = clist.get(i); - ProcessRecord attributedApp = cr.binding.attributedClient; - if (attributedApp == null || attributedApp == pr) { - continue; - } - containsCycle |= attributedApp.mState.isReversedReachable(); - if (attributedApp.mState.isReversedReachable()) { - continue; - } - queue.offer(attributedApp); - attributedApp.mState.setReversedReachable(true); - } - } - } - } + clientProcesses.clear(); + mUnreachedProcessCollector.processList = clientProcesses; + for (int i = 0; i < appsSize; i++) { + final ProcessRecord app = apps.valueAt(i); + app.forEachClient(mUnreachedProcessCollector); } + mUnreachedProcessCollector.processList = null; // Reset the temporary bits. for (int i = clientProcesses.size() - 1; i >= 0; i--) { - clientProcesses.get(i).mState.setReversedReachable(false); + clientProcesses.get(i).mState.setReachable(false); } for (int i = 0, size = apps.size(); i < size; i++) { final ProcessRecord app = apps.valueAt(i); app.mState.setReachable(false); - app.mState.setReversedReachable(false); } - return containsCycle; } @GuardedBy({"mService", "mProcLock"}) @@ -917,10 +855,6 @@ public class OomAdjusterModernImpl extends OomAdjuster { final int procStateTarget = mProcessRecordProcStateNodes.size() - 1; final int adjTarget = mProcessRecordAdjNodes.size() - 1; - final int appUid = !fullUpdate && targetProcesses.size() > 0 - ? targetProcesses.valueAt(0).uid : -1; - final int logUid = mService.mCurOomAdjUid; - mAdjSeq++; // All apps to be updated will be moved to the lowest slot. if (fullUpdate) { @@ -974,7 +908,7 @@ public class OomAdjusterModernImpl extends OomAdjuster { // We don't update the adj list since we're resetting it below. } - // Now nodes are set into their slots, without facting in the bindings. + // Now nodes are set into their slots, without factoring in the bindings. // The nodes between the `lastNode` pointer and the TAIL should be the new nodes. // // The whole rationale here is that, the bindings from client to host app, won't elevate diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 2c6e598ef0a4..b2082d9e8dc0 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -69,8 +69,10 @@ import com.android.server.wm.WindowProcessController; import com.android.server.wm.WindowProcessListener; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; /** * Full information about a particular process that @@ -1613,4 +1615,50 @@ class ProcessRecord implements WindowProcessListener { public boolean wasForceStopped() { return mWasForceStopped; } + + /** + * Traverses all client processes and feed them to consumer. + */ + @GuardedBy("mProcLock") + void forEachClient(@NonNull Consumer<ProcessRecord> consumer) { + for (int i = mServices.numberOfRunningServices() - 1; i >= 0; i--) { + final ServiceRecord s = mServices.getRunningServiceAt(i); + final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = + s.getConnections(); + for (int j = serviceConnections.size() - 1; j >= 0; j--) { + final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j); + for (int k = clist.size() - 1; k >= 0; k--) { + final ConnectionRecord cr = clist.get(k); + consumer.accept(cr.binding.client); + } + } + } + for (int i = mProviders.numberOfProviders() - 1; i >= 0; i--) { + final ContentProviderRecord cpr = mProviders.getProviderAt(i); + for (int j = cpr.connections.size() - 1; j >= 0; j--) { + final ContentProviderConnection conn = cpr.connections.get(j); + consumer.accept(conn.client); + } + } + // If this process is a sandbox itself, also add the app on whose behalf + // its running + if (isSdkSandbox) { + for (int is = mServices.numberOfRunningServices() - 1; is >= 0; is--) { + ServiceRecord s = mServices.getRunningServiceAt(is); + ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = + s.getConnections(); + for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) { + ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni); + for (int i = clist.size() - 1; i >= 0; i--) { + ConnectionRecord cr = clist.get(i); + ProcessRecord attributedApp = cr.binding.attributedClient; + if (attributedApp == null || attributedApp == this) { + continue; + } + consumer.accept(attributedApp); + } + } + } + } + } } diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 8723c5d549e4..5ad921fd0bae 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -379,12 +379,6 @@ final class ProcessStateRecord { private boolean mReachable; /** - * Whether or not this process is reversed reachable from given process. - */ - @GuardedBy("mService") - private boolean mReversedReachable; - - /** * The most recent time when the last visible activity within this process became invisible. * * <p> It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is @@ -997,16 +991,6 @@ final class ProcessStateRecord { } @GuardedBy("mService") - boolean isReversedReachable() { - return mReversedReachable; - } - - @GuardedBy("mService") - void setReversedReachable(boolean reversedReachable) { - mReversedReachable = reversedReachable; - } - - @GuardedBy("mService") void resetCachedInfo() { mCachedHasActivities = VALUE_INVALID; mCachedIsHeavyWeight = VALUE_INVALID; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 1ef4333ddbd8..df106a794041 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -154,7 +154,6 @@ import android.media.permission.SafeCloseable; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.media.projection.IMediaProjectionManager; -import android.media.session.MediaSessionManager; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -312,9 +311,6 @@ public class AudioService extends IAudioService.Stub private final ContentResolver mContentResolver; private final AppOpsManager mAppOps; - /** do not use directly, use getMediaSessionManager() which handles lazy initialization */ - @Nullable private volatile MediaSessionManager mMediaSessionManager; - // the platform type affects volume and silent mode behavior private final int mPlatformType; @@ -945,8 +941,6 @@ public class AudioService extends IAudioService.Stub private final SoundDoseHelper mSoundDoseHelper; - private final HardeningEnforcer mHardeningEnforcer; - private final Object mSupportedSystemUsagesLock = new Object(); @GuardedBy("mSupportedSystemUsagesLock") private @AttributeSystemUsage int[] mSupportedSystemUsages = @@ -1321,8 +1315,6 @@ public class AudioService extends IAudioService.Stub mDisplayManager = context.getSystemService(DisplayManager.class); mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler); - - mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive()); } private void initVolumeStreamStates() { @@ -1394,6 +1386,7 @@ public class AudioService extends IAudioService.Stub // check on volume initialization checkVolumeRangeInitialization("AudioService()"); + } private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener = @@ -1406,14 +1399,6 @@ public class AudioService extends IAudioService.Stub } }; - private MediaSessionManager getMediaSessionManager() { - if (mMediaSessionManager == null) { - mMediaSessionManager = (MediaSessionManager) mContext - .getSystemService(Context.MEDIA_SESSION_SERVICE); - } - return mMediaSessionManager; - } - /** * Initialize intent receives and settings observers for this service. * Must be called after createStreamStates() as the handling of some events @@ -3442,10 +3427,6 @@ public class AudioService extends IAudioService.Stub * Part of service interface, check permissions here */ public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags, String callingPackage, String attributionTag) { - if (mHardeningEnforcer.blockVolumeMethod( - HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) { - return; - } if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) { Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without" + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage); @@ -4222,10 +4203,6 @@ public class AudioService extends IAudioService.Stub * Part of service interface, check permissions here */ public void setStreamVolumeWithAttribution(int streamType, int index, int flags, String callingPackage, String attributionTag) { - if (mHardeningEnforcer.blockVolumeMethod( - HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) { - return; - } setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null, callingPackage, attributionTag); } @@ -5080,7 +5057,6 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#setMasterMute(boolean, int) */ public void setMasterMute(boolean mute, int flags, String callingPackage, int userId, String attributionTag) { - super.setMasterMute_enforcePermission(); setMasterMuteInternal(mute, flags, callingPackage, @@ -5446,10 +5422,6 @@ public class AudioService extends IAudioService.Stub } public void setRingerModeExternal(int ringerMode, String caller) { - if (mHardeningEnforcer.blockVolumeMethod( - HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) { - return; - } if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode) && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) { throw new SecurityException("Not allowed to change Do Not Disturb state"); @@ -6202,35 +6174,6 @@ public class AudioService extends IAudioService.Stub AudioDeviceVolumeManager.ADJUST_MODE_NORMAL); } - /** - * @see AudioManager#adjustVolume(int, int) - * This method is redirected from AudioManager to AudioService for API hardening rules - * enforcement then to MediaSession for implementation. - */ - @Override - public void adjustVolume(int direction, int flags) { - if (mHardeningEnforcer.blockVolumeMethod( - HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) { - return; - } - getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, - direction, flags); - } - - /** - * @see AudioManager#adjustSuggestedStreamVolume(int, int, int) - * This method is redirected from AudioManager to AudioService for API hardening rules - * enforcement then to MediaSession for implementation. - */ - @Override - public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { - if (mHardeningEnforcer.blockVolumeMethod( - HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) { - return; - } - getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags); - } - /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */ @Override public void setStreamVolumeForUid(int streamType, int index, int flags, diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java deleted file mode 100644 index 4ceb83b2e1c9..000000000000 --- a/services/core/java/com/android/server/audio/HardeningEnforcer.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2023 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.audio; - -import static android.media.audio.Flags.autoPublicVolumeApiHardening; - -import android.Manifest; -import android.content.Context; -import android.content.pm.PackageManager; -import android.media.AudioManager; -import android.os.Binder; -import android.os.UserHandle; -import android.text.TextUtils; -import android.util.Log; - -/** - * Class to encapsulate all audio API hardening operations - */ -public class HardeningEnforcer { - - private static final String TAG = "AS.HardeningEnforcer"; - - final Context mContext; - final boolean mIsAutomotive; - - /** - * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)} - */ - public static final int METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME = 100; - /** - * Matches calls from {@link AudioManager#adjustVolume(int, int)} - */ - public static final int METHOD_AUDIO_MANAGER_ADJUST_VOLUME = 101; - /** - * Matches calls from {@link AudioManager#adjustSuggestedStreamVolume(int, int, int)} - */ - public static final int METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME = 102; - /** - * Matches calls from {@link AudioManager#adjustStreamVolume(int, int, int)} - */ - public static final int METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME = 103; - /** - * Matches calls from {@link AudioManager#setRingerMode(int)} - */ - public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200; - - public HardeningEnforcer(Context ctxt, boolean isAutomotive) { - mContext = ctxt; - mIsAutomotive = isAutomotive; - } - - /** - * Checks whether the call in the current thread should be allowed or blocked - * @param volumeMethod name of the method to check, for logging purposes - * @return false if the method call is allowed, true if it should be a no-op - */ - protected boolean blockVolumeMethod(int volumeMethod) { - // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED - if (mIsAutomotive) { - if (!autoPublicVolumeApiHardening()) { - // automotive hardening flag disabled, no blocking on auto - return false; - } - if (mContext.checkCallingOrSelfPermission( - Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) - == PackageManager.PERMISSION_GRANTED) { - return false; - } - if (Binder.getCallingUid() < UserHandle.AID_APP_START) { - return false; - } - // TODO metrics? - // TODO log for audio dumpsys? - Log.e(TAG, "Preventing volume method " + volumeMethod + " for " - + getPackNameForUid(Binder.getCallingUid())); - return true; - } - // not blocking - return false; - } - - private String getPackNameForUid(int uid) { - final long token = Binder.clearCallingIdentity(); - try { - final String[] names = mContext.getPackageManager().getPackagesForUid(uid); - if (names == null - || names.length == 0 - || TextUtils.isEmpty(names[0])) { - return "[" + uid + "]"; - } - return names[0]; - } finally { - Binder.restoreCallingIdentity(token); - } - } -} diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index ea92154f2df0..61e4f36a34ed 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -19,6 +19,9 @@ package com.android.server.audio; import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES; import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN; import static android.media.AudioSystem.isBluetoothDevice; +import static android.media.AudioSystem.isBluetoothLeDevice; + +import static com.android.media.audio.Flags.dsaOverBtLeAudio; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1625,10 +1628,10 @@ public class SpatializerHelper { } private int getHeadSensorHandleUpdateTracker() { - int headHandle = -1; + Sensor htSensor = null; if (sRoutingDevices.isEmpty()) { logloge("getHeadSensorHandleUpdateTracker: no device, no head tracker"); - return headHandle; + return -1; } final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); List<String> deviceAddresses = mAudioService.getDeviceAddresses(currentDevice); @@ -1642,27 +1645,86 @@ public class SpatializerHelper { for (String address : deviceAddresses) { UUID routingDeviceUuid = UuidUtils.uuidFromAudioDeviceAttributes( new AudioDeviceAttributes(currentDevice.getInternalType(), address)); - for (Sensor sensor : sensors) { - final UUID uuid = sensor.getUuid(); - if (uuid.equals(routingDeviceUuid)) { - headHandle = sensor.getHandle(); - if (!setHasHeadTracker(currentDevice)) { - headHandle = -1; + if (dsaOverBtLeAudio()) { + for (Sensor sensor : sensors) { + final UUID uuid = sensor.getUuid(); + if (uuid.equals(routingDeviceUuid)) { + htSensor = sensor; + HeadtrackerInfo info = new HeadtrackerInfo(sensor); + if (isBluetoothLeDevice(currentDevice.getInternalType())) { + if (info.getMajorVersion() == 2) { + // Version 2 is used only by LE Audio profile + break; + } + // we do not break, as this could be a match on the A2DP sensor + // for a dual mode headset. + } else if (info.getMajorVersion() == 1) { + // Version 1 is used only by A2DP profile + break; + } + } + if (htSensor == null && uuid.equals(UuidUtils.STANDALONE_UUID)) { + htSensor = sensor; + // we do not break, perhaps we find a head tracker on device. } - break; } - if (uuid.equals(UuidUtils.STANDALONE_UUID)) { - headHandle = sensor.getHandle(); - // we do not break, perhaps we find a head tracker on device. + if (htSensor != null) { + if (htSensor.getUuid().equals(UuidUtils.STANDALONE_UUID)) { + break; + } + if (setHasHeadTracker(currentDevice)) { + break; + } else { + htSensor = null; + } + } + } else { + for (Sensor sensor : sensors) { + final UUID uuid = sensor.getUuid(); + if (uuid.equals(routingDeviceUuid)) { + htSensor = sensor; + if (!setHasHeadTracker(currentDevice)) { + htSensor = null; + } + break; + } + if (uuid.equals(UuidUtils.STANDALONE_UUID)) { + htSensor = sensor; + // we do not break, perhaps we find a head tracker on device. + } + } + if (htSensor != null) { + break; } - } - if (headHandle != -1) { - break; } } - return headHandle; + return htSensor != null ? htSensor.getHandle() : -1; } + /** + * Contains the information parsed from the head tracker sensor version. + * See platform/hardware/libhardware/modules/sensors/dynamic_sensor/HidRawSensor.h + * for the definition of version and capability fields. + */ + private static class HeadtrackerInfo { + private final int mVersion; + HeadtrackerInfo(Sensor sensor) { + mVersion = sensor.getVersion(); + } + int getMajorVersion() { + return (mVersion & 0xFF000000) >> 24; + } + int getMinorVersion() { + return (mVersion & 0xFF0000) >> 16; + } + boolean hasAclTransport() { + return getMajorVersion() == 2 ? ((mVersion & 0x1) != 0) : false; + } + boolean hasIsoTransport() { + return getMajorVersion() == 2 ? ((mVersion & 0x2) != 0) : false; + } + }; + private int getScreenSensorHandle() { int screenHandle = -1; Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index b9ccbfbf55e7..c5073001a672 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -576,7 +576,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onDialogAnimatedIn(boolean startFingerprintNow) { - if (mState != STATE_AUTH_STARTED) { + if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI) { Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState); return; } diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java index c629b2b91603..be78ea20d09d 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -157,4 +157,10 @@ public abstract class VirtualDeviceManagerInternal { * @see VirtualDevice#getPersistentDeviceId() */ public abstract @Nullable String getPersistentIdForDevice(int deviceId); + + /** + * Returns all current persistent device IDs, including the ones for which no virtual device + * exists, as long as one may have existed or can be created. + */ + public abstract @NonNull Set<String> getAllPersistentDeviceIds(); } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index fac727f17283..dff14b5fbdd0 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -1114,12 +1114,22 @@ public final class DeviceStateManagerService extends SystemService { public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) { mHandler.post(() -> { + boolean tracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER); + if (tracingEnabled) { // To avoid creating the string when not needed. + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, + "notifyDeviceStateInfoAsync(pid=" + mPid + ")"); + } try { mCallback.onDeviceStateInfoChanged(info); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.", ex); } + finally { + if (tracingEnabled) { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } }); } diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java index 669580189d7c..9fcaa1e2af16 100644 --- a/services/core/java/com/android/server/display/DisplayBrightnessState.java +++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java @@ -38,6 +38,7 @@ public final class DisplayBrightnessState { private final boolean mShouldUseAutoBrightness; private final boolean mIsSlowChange; + private final boolean mShouldUpdateScreenBrightnessSetting; private final float mCustomAnimationRate; @@ -50,6 +51,7 @@ public final class DisplayBrightnessState { mIsSlowChange = builder.isSlowChange(); mMaxBrightness = builder.getMaxBrightness(); mCustomAnimationRate = builder.getCustomAnimationRate(); + mShouldUpdateScreenBrightnessSetting = builder.shouldUpdateScreenBrightnessSetting(); } /** @@ -109,6 +111,13 @@ public final class DisplayBrightnessState { return mCustomAnimationRate; } + /** + * @return {@code true} if the screen brightness setting should be updated + */ + public boolean shouldUpdateScreenBrightnessSetting() { + return mShouldUpdateScreenBrightnessSetting; + } + @Override public String toString() { StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:"); @@ -123,6 +132,8 @@ public final class DisplayBrightnessState { stringBuilder.append("\n isSlowChange:").append(mIsSlowChange); stringBuilder.append("\n maxBrightness:").append(mMaxBrightness); stringBuilder.append("\n customAnimationRate:").append(mCustomAnimationRate); + stringBuilder.append("\n shouldUpdateScreenBrightnessSetting:") + .append(mShouldUpdateScreenBrightnessSetting); return stringBuilder.toString(); } @@ -149,13 +160,16 @@ public final class DisplayBrightnessState { && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness() && mIsSlowChange == otherState.isSlowChange() && mMaxBrightness == otherState.getMaxBrightness() - && mCustomAnimationRate == otherState.getCustomAnimationRate(); + && mCustomAnimationRate == otherState.getCustomAnimationRate() + && mShouldUpdateScreenBrightnessSetting + == otherState.shouldUpdateScreenBrightnessSetting(); } @Override public int hashCode() { return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason, - mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate); + mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate, + mShouldUpdateScreenBrightnessSetting); } /** @@ -177,6 +191,7 @@ public final class DisplayBrightnessState { private boolean mIsSlowChange; private float mMaxBrightness; private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET; + private boolean mShouldUpdateScreenBrightnessSetting; /** * Create a builder starting with the values from the specified {@link @@ -194,6 +209,8 @@ public final class DisplayBrightnessState { builder.setIsSlowChange(state.isSlowChange()); builder.setMaxBrightness(state.getMaxBrightness()); builder.setCustomAnimationRate(state.getCustomAnimationRate()); + builder.setShouldUpdateScreenBrightnessSetting( + state.shouldUpdateScreenBrightnessSetting()); return builder; } @@ -290,8 +307,8 @@ public final class DisplayBrightnessState { /** * See {@link DisplayBrightnessState#isSlowChange()}. */ - public Builder setIsSlowChange(boolean shouldUseAutoBrightness) { - this.mIsSlowChange = shouldUseAutoBrightness; + public Builder setIsSlowChange(boolean isSlowChange) { + this.mIsSlowChange = isSlowChange; return this; } @@ -334,6 +351,22 @@ public final class DisplayBrightnessState { } /** + * See {@link DisplayBrightnessState#shouldUpdateScreenBrightnessSetting()}. + */ + public boolean shouldUpdateScreenBrightnessSetting() { + return mShouldUpdateScreenBrightnessSetting; + } + + /** + * See {@link DisplayBrightnessState#shouldUpdateScreenBrightnessSetting()}. + */ + public Builder setShouldUpdateScreenBrightnessSetting( + boolean shouldUpdateScreenBrightnessSetting) { + mShouldUpdateScreenBrightnessSetting = shouldUpdateScreenBrightnessSetting; + return this; + } + + /** * This is used to construct an immutable DisplayBrightnessState object from its builder */ public DisplayBrightnessState build() { diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 9f4f78794659..2fdf90d7d286 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -209,7 +209,7 @@ abstract class DisplayDevice { int state, float brightnessState, float sdrBrightnessState, - @Nullable DisplayOffloadSession displayOffloadSession) { + @Nullable DisplayOffloadSessionImpl displayOffloadSession) { return null; } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 11f4e5f75b11..e99f82aee96d 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -4948,20 +4948,8 @@ public final class DisplayManagerService extends SystemService { return null; } - DisplayOffloadSession session = - new DisplayOffloadSession() { - @Override - public void setDozeStateOverride(int displayState) { - synchronized (mSyncRoot) { - displayPowerController.overrideDozeScreenState(displayState); - } - } - - @Override - public DisplayOffloader getDisplayOffloader() { - return displayOffloader; - } - }; + DisplayOffloadSessionImpl session = new DisplayOffloadSessionImpl(displayOffloader, + displayPowerController); logicalDisplay.setDisplayOffloadSessionLocked(session); displayPowerController.setDisplayOffloadSession(session); return session; diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java new file mode 100644 index 000000000000..1bd556bdcc4f --- /dev/null +++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 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.annotation.Nullable; +import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; +import android.os.Trace; + +/** + * An implementation of the offload session that keeps track of whether the session is active. + * An offload session is used to control the display's brightness using the offload chip. + */ +public class DisplayOffloadSessionImpl implements DisplayManagerInternal.DisplayOffloadSession { + + @Nullable + private final DisplayManagerInternal.DisplayOffloader mDisplayOffloader; + private final DisplayPowerControllerInterface mDisplayPowerController; + private boolean mIsActive; + + public DisplayOffloadSessionImpl( + @Nullable DisplayManagerInternal.DisplayOffloader displayOffloader, + DisplayPowerControllerInterface displayPowerController) { + mDisplayOffloader = displayOffloader; + mDisplayPowerController = displayPowerController; + } + + @Override + public void setDozeStateOverride(int displayState) { + mDisplayPowerController.overrideDozeScreenState(displayState); + } + + @Override + public boolean isActive() { + return mIsActive; + } + + @Override + public void updateBrightness(float brightness) { + if (mIsActive) { + mDisplayPowerController.setBrightnessFromOffload(brightness); + } + } + + /** + * Start the offload session. The method returns if the session is already active. + * @return Whether the session was started successfully + */ + public boolean startOffload() { + if (mDisplayOffloader == null || mIsActive) { + return false; + } + Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload"); + try { + return mIsActive = mDisplayOffloader.startOffload(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } + } + + /** + * Stop the offload session. The method returns if the session is not active. + */ + public void stopOffload() { + if (mDisplayOffloader == null || !mIsActive) { + return; + } + Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#stopOffload"); + try { + mDisplayOffloader.stopOffload(); + mIsActive = false; + mDisplayPowerController.setBrightnessFromOffload(PowerManager.BRIGHTNESS_INVALID_FLOAT); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } + } +} diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 5761c31b29bf..f3d761a7372b 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -2224,6 +2224,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } @Override + public void setBrightnessFromOffload(float brightness) { + // The old DPC is no longer supported + } + + @Override public BrightnessInfo getBrightnessInfo() { synchronized (mCachedBrightnessInfo) { return new BrightnessInfo( diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index a6155da86f9a..d4e0cbb126ed 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -151,6 +151,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private static final int MSG_SET_DWBC_STRONG_MODE = 14; private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15; private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16; + private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17; @@ -562,7 +563,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal new DisplayBrightnessController(context, null, mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, brightnessSetting, () -> postBrightnessChangeRunnable(), - new HandlerExecutor(mHandler)); + new HandlerExecutor(mHandler), flags); mBrightnessClamperController = mInjector.getBrightnessClamperController( mHandler, modeChangeCallback::run, @@ -1394,7 +1395,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); - boolean updateScreenBrightnessSetting = false; + boolean updateScreenBrightnessSetting = + displayBrightnessState.shouldUpdateScreenBrightnessSetting(); float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness(); // Apply auto-brightness. int brightnessAdjustmentFlags = 0; @@ -1854,6 +1856,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } @Override + public void setBrightnessFromOffload(float brightness) { + Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD, + Float.floatToIntBits(brightness), 0 /*unused*/); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); + } + + @Override public BrightnessInfo getBrightnessInfo() { synchronized (mCachedBrightnessInfo) { return new BrightnessInfo( @@ -2886,6 +2895,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal case MSG_SET_DWBC_LOGGING_ENABLED: setDwbcLoggingEnabled(msg.arg1); break; + case MSG_SET_BRIGHTNESS_FROM_OFFLOAD: + mDisplayBrightnessController.setBrightnessFromOffload( + Float.intBitsToFloat(msg.arg1)); + updatePowerState(); + break; } } } diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java index 181386a93c71..72079a4c82fe 100644 --- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java +++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java @@ -22,6 +22,7 @@ import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; import java.io.PrintWriter; @@ -150,6 +151,14 @@ public interface DisplayPowerControllerInterface { void setTemporaryAutoBrightnessAdjustment(float adjustment); /** + * Sets temporary brightness from the offload chip until we get a brightness value from + * the light sensor. + * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and + * {@link PowerManager.BRIGHTNESS_MAX}. Values outside of that range will be ignored. + */ + void setBrightnessFromOffload(float brightness); + + /** * Gets the screen brightness setting */ float getScreenBrightnessSetting(); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index be3207dfb4ee..ff9a1ab61b13 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -19,11 +19,11 @@ package com.android.server.display; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.Mode.INVALID_MODE_ID; +import android.annotation.Nullable; import android.app.ActivityThread; import android.content.Context; import android.content.res.Resources; import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession; -import android.hardware.display.DisplayManagerInternal.DisplayOffloader; import android.hardware.sidekick.SidekickInternal; import android.os.Build; import android.os.Handler; @@ -238,7 +238,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mAllmRequested; private boolean mGameContentTypeRequested; private boolean mSidekickActive; - private boolean mDisplayOffloadActive; private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo; // The supported display modes according to SurfaceFlinger private SurfaceControl.DisplayMode[] mSfDisplayModes; @@ -765,7 +764,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { final int state, final float brightnessState, final float sdrBrightnessState, - DisplayOffloadSession displayOffloadSession) { + @Nullable DisplayOffloadSessionImpl displayOffloadSession) { // Assume that the brightness is off if the display is being turned off. assert state != Display.STATE_OFF @@ -832,25 +831,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { + ", state=" + Display.stateToString(state) + ")"); } - DisplayOffloader displayOffloader = - displayOffloadSession == null - ? null - : displayOffloadSession.getDisplayOffloader(); - boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled(); // We must tell sidekick/displayoffload to stop controlling the display // before we can change its power mode, so do that first. if (isDisplayOffloadEnabled) { - if (mDisplayOffloadActive && displayOffloader != null) { - Trace.traceBegin(Trace.TRACE_TAG_POWER, - "DisplayOffloader#stopOffload"); - try { - displayOffloader.stopOffload(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); - } - mDisplayOffloadActive = false; + if (displayOffloadSession != null) { + displayOffloadSession.stopOffload(); } } else { if (mSidekickActive) { @@ -881,16 +868,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { // have a sidekick/displayoffload available, tell it now that it can take // control. if (isDisplayOffloadEnabled) { - if (DisplayOffloadSession.isSupportedOffloadState(state) && - displayOffloader != null - && !mDisplayOffloadActive) { - Trace.traceBegin( - Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload"); - try { - mDisplayOffloadActive = displayOffloader.startOffload(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); - } + if (DisplayOffloadSession.isSupportedOffloadState(state) + && displayOffloadSession != null) { + displayOffloadSession.startOffload(); } } else { if (Display.isSuspendedState(state) && state != Display.STATE_OFF diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 3d4209e0d6f3..ba321ae5d807 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -138,7 +138,7 @@ final class LogicalDisplay { private final Rect mTempDisplayRect = new Rect(); /** A session token that controls the offloading operations of this logical display. */ - private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; + private DisplayOffloadSessionImpl mDisplayOffloadSession; /** * Name of a display group to which the display is assigned. @@ -969,12 +969,11 @@ final class LogicalDisplay { return mDisplayGroupName; } - public void setDisplayOffloadSessionLocked( - DisplayManagerInternal.DisplayOffloadSession session) { + public void setDisplayOffloadSessionLocked(DisplayOffloadSessionImpl session) { mDisplayOffloadSession = session; } - public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSessionLocked() { + public DisplayOffloadSessionImpl getDisplayOffloadSessionLocked() { return mDisplayOffloadSession; } diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 90e32a685a34..edbd42465534 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -43,7 +43,6 @@ import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED; import android.annotation.Nullable; import android.content.Context; import android.graphics.Point; -import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; import android.media.projection.IMediaProjection; @@ -380,7 +379,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { @Override public Runnable requestDisplayStateLocked(int state, float brightnessState, - float sdrBrightnessState, DisplayOffloadSession displayOffloadSession) { + float sdrBrightnessState, DisplayOffloadSessionImpl displayOffloadSession) { if (state != mDisplayState) { mDisplayState = state; if (state == Display.STATE_OFF) { diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java index d7ae2699ee2d..8fe5f213d766 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java @@ -39,7 +39,8 @@ public final class BrightnessReason { public static final int REASON_BOOST = 8; public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9; public static final int REASON_FOLLOWER = 10; - public static final int REASON_MAX = REASON_FOLLOWER; + public static final int REASON_OFFLOAD = 11; + public static final int REASON_MAX = REASON_OFFLOAD; public static final int MODIFIER_DIMMED = 0x1; public static final int MODIFIER_LOW_POWER = 0x2; @@ -196,6 +197,8 @@ public final class BrightnessReason { return "screen_off_brightness_sensor"; case REASON_FOLLOWER: return "follower"; + case REASON_OFFLOAD: + return "offload"; default: return Integer.toString(reason); } diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index d6f0098c13cb..617befbbd17d 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -31,6 +31,7 @@ import com.android.server.display.BrightnessSetting; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy; +import com.android.server.display.feature.DisplayManagerFlags; import java.io.PrintWriter; @@ -104,7 +105,8 @@ public final class DisplayBrightnessController { */ public DisplayBrightnessController(Context context, Injector injector, int displayId, float defaultScreenBrightness, BrightnessSetting brightnessSetting, - Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor) { + Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor, + DisplayManagerFlags flags) { if (injector == null) { injector = new Injector(); } @@ -116,7 +118,7 @@ public final class DisplayBrightnessController { mCurrentScreenBrightness = getScreenBrightnessSetting(); mOnBrightnessChangeRunnable = onBrightnessChangeRunnable; mDisplayBrightnessStrategySelector = injector.getDisplayBrightnessStrategySelector(context, - displayId); + displayId, flags); mBrightnessChangeExecutor = brightnessChangeExecutor; mPersistBrightnessNitsForDefaultDisplay = context.getResources().getBoolean( com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay); @@ -172,6 +174,18 @@ public final class DisplayBrightnessController { } /** + * Sets the brightness from the offload session. + */ + public void setBrightnessFromOffload(float brightness) { + synchronized (mLock) { + if (mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy() != null) { + mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy() + .setOffloadScreenBrightness(brightness); + } + } + } + + /** * Returns a boolean flag indicating if the light sensor is to be used to decide the screen * brightness when dozing */ @@ -423,8 +437,9 @@ public final class DisplayBrightnessController { @VisibleForTesting static class Injector { DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(Context context, - int displayId) { - return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId); + int displayId, DisplayManagerFlags flags) { + return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId, + flags); } } diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index f141c20158cd..055f94a23363 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -17,6 +17,7 @@ package com.android.server.display.brightness; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.display.DisplayManagerInternal; import android.util.IndentingPrintWriter; @@ -31,9 +32,11 @@ import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy; import com.android.server.display.brightness.strategy.DozeBrightnessStrategy; import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy; import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy; +import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy; import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy; import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy; import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy; +import com.android.server.display.feature.DisplayManagerFlags; import java.io.PrintWriter; @@ -63,6 +66,10 @@ public class DisplayBrightnessStrategySelector { private final InvalidBrightnessStrategy mInvalidBrightnessStrategy; // Controls brightness when automatic (adaptive) brightness is running. private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy; + // Controls the brightness if adaptive brightness is on and there exists an active offload + // session. Brightness value is provided by the offload session. + @Nullable + private final OffloadBrightnessStrategy mOffloadBrightnessStrategy; // We take note of the old brightness strategy so that we can know when the strategy changes. private String mOldBrightnessStrategyName; @@ -72,7 +79,8 @@ public class DisplayBrightnessStrategySelector { /** * The constructor of DozeBrightnessStrategy. */ - public DisplayBrightnessStrategySelector(Context context, Injector injector, int displayId) { + public DisplayBrightnessStrategySelector(Context context, Injector injector, int displayId, + DisplayManagerFlags flags) { if (injector == null) { injector = new Injector(); } @@ -85,6 +93,11 @@ public class DisplayBrightnessStrategySelector { mFollowerBrightnessStrategy = injector.getFollowerBrightnessStrategy(displayId); mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy(); mAutomaticBrightnessStrategy = injector.getAutomaticBrightnessStrategy(context, displayId); + if (flags.isDisplayOffloadEnabled()) { + mOffloadBrightnessStrategy = injector.getOffloadBrightnessStrategy(); + } else { + mOffloadBrightnessStrategy = null; + } mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean( R.bool.config_allowAutoBrightnessWhileDozing); mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName(); @@ -114,6 +127,9 @@ public class DisplayBrightnessStrategySelector { } else if (BrightnessUtils.isValidBrightnessValue( mTemporaryBrightnessStrategy.getTemporaryScreenBrightness())) { displayBrightnessStrategy = mTemporaryBrightnessStrategy; + } else if (mOffloadBrightnessStrategy != null && BrightnessUtils.isValidBrightnessValue( + mOffloadBrightnessStrategy.getOffloadScreenBrightness())) { + displayBrightnessStrategy = mOffloadBrightnessStrategy; } if (!mOldBrightnessStrategyName.equals(displayBrightnessStrategy.getName())) { @@ -138,6 +154,11 @@ public class DisplayBrightnessStrategySelector { return mAutomaticBrightnessStrategy; } + @Nullable + public OffloadBrightnessStrategy getOffloadBrightnessStrategy() { + return mOffloadBrightnessStrategy; + } + /** * Returns a boolean flag indicating if the light sensor is to be used to decide the screen * brightness when dozing @@ -159,6 +180,9 @@ public class DisplayBrightnessStrategySelector { + mAllowAutoBrightnessWhileDozingConfig); IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); mTemporaryBrightnessStrategy.dump(ipw); + if (mOffloadBrightnessStrategy != null) { + mOffloadBrightnessStrategy.dump(ipw); + } } /** @@ -210,5 +234,9 @@ public class DisplayBrightnessStrategySelector { AutomaticBrightnessStrategy getAutomaticBrightnessStrategy(Context context, int displayId) { return new AutomaticBrightnessStrategy(context, displayId); } + + OffloadBrightnessStrategy getOffloadBrightnessStrategy() { + return new OffloadBrightnessStrategy(); + } } } diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index bcd52598edd2..3c23b5c10671 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -107,6 +107,7 @@ public class AutomaticBrightnessStrategy { mIsAutoBrightnessEnabled = shouldUseAutoBrightness() && (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze) && brightnessReason != BrightnessReason.REASON_OVERRIDE + && brightnessReason != BrightnessReason.REASON_OFFLOAD && mAutomaticBrightnessController != null; mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness() && !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze); diff --git a/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java new file mode 100644 index 000000000000..55f8914e26f6 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 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.brightness.strategy; + +import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; + +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; + +import java.io.PrintWriter; + +/** + * Manages the brightness of the display when auto-brightness is on, the screen has just turned on + * and there is no available lux reading yet. The brightness value is read from the offload chip. + */ +public class OffloadBrightnessStrategy implements DisplayBrightnessStrategy { + + private float mOffloadScreenBrightness; + + public OffloadBrightnessStrategy() { + mOffloadScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + } + + @Override + public DisplayBrightnessState updateBrightness( + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) { + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_OFFLOAD); + return new DisplayBrightnessState.Builder() + .setBrightness(mOffloadScreenBrightness) + .setSdrBrightness(mOffloadScreenBrightness) + .setBrightnessReason(brightnessReason) + .setDisplayBrightnessStrategyName(getName()) + .setIsSlowChange(false) + .setShouldUpdateScreenBrightnessSetting(true) + .build(); + } + + @Override + public String getName() { + return "OffloadBrightnessStrategy"; + } + + public float getOffloadScreenBrightness() { + return mOffloadScreenBrightness; + } + + public void setOffloadScreenBrightness(float offloadScreenBrightness) { + mOffloadScreenBrightness = offloadScreenBrightness; + } + + /** + * Dumps the state of this class. + */ + public void dump(PrintWriter writer) { + writer.println("OffloadBrightnessStrategy:"); + writer.println(" mOffloadScreenBrightness:" + mOffloadScreenBrightness); + } +} diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java index 360a6a721988..6bdfae2dc02f 100644 --- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java +++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java @@ -110,7 +110,7 @@ import java.util.Objects; @Override @NonNull - public synchronized MediaRoute2Info getDeviceRoute() { + public synchronized MediaRoute2Info getSelectedRoute() { if (mSelectedRoute != null) { return mSelectedRoute; } diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java index 7876095a548a..0fdaaa7604e5 100644 --- a/services/core/java/com/android/server/media/DeviceRouteController.java +++ b/services/core/java/com/android/server/media/DeviceRouteController.java @@ -72,13 +72,9 @@ import com.android.media.flags.Flags; */ boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type); - /** - * Returns currently selected device (built-in or wired) route. - * - * @return non-null device route. - */ + /** Returns the currently selected device (built-in or wired) route. */ @NonNull - MediaRoute2Info getDeviceRoute(); + MediaRoute2Info getSelectedRoute(); /** * Updates device route volume. diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java index 6ba40ae33f3c..65874e23dcdc 100644 --- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java +++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java @@ -107,7 +107,7 @@ import java.util.Objects; @Override @NonNull - public synchronized MediaRoute2Info getDeviceRoute() { + public synchronized MediaRoute2Info getSelectedRoute() { return mDeviceRoute; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index c24d6a054236..994d3ca1124f 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -76,6 +76,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import android.util.Slog; import android.view.KeyEvent; import com.android.server.LocalServices; @@ -348,16 +349,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } else { if (mVolumeControlType == VOLUME_CONTROL_FIXED) { if (DEBUG) { - Log.d(TAG, "Session does not support volume adjustment"); + Slog.d(TAG, "Session does not support volume adjustment"); } } else if (direction == AudioManager.ADJUST_TOGGLE_MUTE || direction == AudioManager.ADJUST_MUTE || direction == AudioManager.ADJUST_UNMUTE) { - Log.w(TAG, "Muting remote playback is not supported"); + Slog.w(TAG, "Muting remote playback is not supported"); } else { if (DEBUG) { - Log.w(TAG, "adjusting volume, pkg=" + packageName + ", asSystemService=" - + asSystemService + ", dir=" + direction); + Slog.w( + TAG, + "adjusting volume, pkg=" + packageName + + ", asSystemService=" + asSystemService + + ", dir=" + direction); } mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction); @@ -371,8 +375,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } if (DEBUG) { - Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is " - + mMaxVolume); + Slog.d( + TAG, + "Adjusted optimistic volume to " + mOptimisticVolume + + " max is " + mMaxVolume); } } // Always notify, even if the volume hasn't changed. This is important to ensure that @@ -388,23 +394,33 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (mVolumeType == PLAYBACK_TYPE_LOCAL) { int stream = getVolumeStream(mAudioAttrs); final int volumeValue = value; - mHandler.post(new Runnable() { - @Override - public void run() { - try { - mAudioManager.setStreamVolumeForUid(stream, volumeValue, flags, - opPackageName, uid, pid, - mContext.getApplicationInfo().targetSdkVersion); - } catch (IllegalArgumentException | SecurityException e) { - Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue - + ", flags=" + flags, e); - } - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + try { + mAudioManager.setStreamVolumeForUid( + stream, + volumeValue, + flags, + opPackageName, + uid, + pid, + mContext.getApplicationInfo().targetSdkVersion); + } catch (IllegalArgumentException | SecurityException e) { + Slog.e( + TAG, + "Cannot set volume: stream=" + stream + + ", value=" + volumeValue + + ", flags=" + flags, + e); + } + } + }); } else { if (mVolumeControlType != VOLUME_CONTROL_ABSOLUTE) { if (DEBUG) { - Log.d(TAG, "Session does not support setting volume"); + Slog.d(TAG, "Session does not support setting volume"); } } else { value = Math.max(0, Math.min(value, mMaxVolume)); @@ -419,8 +435,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } if (DEBUG) { - Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is " - + mMaxVolume); + Slog.d( + TAG, + "Set optimistic volume to " + mOptimisticVolume + + " max is " + mMaxVolume); } } // Always notify, even if the volume hasn't changed. @@ -528,25 +546,25 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public boolean canHandleVolumeKey() { if (isPlaybackTypeLocal()) { if (DEBUG) { - Log.d(TAG, "Local MediaSessionRecord can handle volume key"); + Slog.d(TAG, "Local MediaSessionRecord can handle volume key"); } return true; } if (mVolumeControlType == VOLUME_CONTROL_FIXED) { if (DEBUG) { - Log.d( + Slog.d( TAG, "Local MediaSessionRecord with FIXED volume control can't handle volume" - + " key"); + + " key"); } return false; } if (mVolumeAdjustmentForRemoteGroupSessions) { if (DEBUG) { - Log.d( + Slog.d( TAG, "Volume adjustment for remote group sessions allowed so MediaSessionRecord" - + " can handle volume key"); + + " can handle volume key"); } return true; } @@ -556,7 +574,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR boolean foundNonSystemSession = false; boolean remoteSessionAllowVolumeAdjustment = true; if (DEBUG) { - Log.d( + Slog.d( TAG, "Found " + sessions.size() @@ -565,7 +583,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } for (RoutingSessionInfo session : sessions) { if (DEBUG) { - Log.d(TAG, "Found routingSessionInfo: " + session); + Slog.d(TAG, "Found routingSessionInfo: " + session); } if (!session.isSystemSession()) { foundNonSystemSession = true; @@ -576,10 +594,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } if (!foundNonSystemSession) { if (DEBUG) { - Log.d( + Slog.d( TAG, - "Package " - + mPackageName + "Package " + mPackageName + " has a remote media session but no associated routing session"); } } @@ -669,8 +686,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR final boolean asSystemService, final boolean useSuggested, final int previousFlagPlaySound) { if (DEBUG) { - Log.w(TAG, "adjusting local volume, stream=" + stream + ", dir=" + direction - + ", asSystemService=" + asSystemService + ", useSuggested=" + useSuggested); + Slog.w( + TAG, + "adjusting local volume, stream=" + stream + ", dir=" + direction + + ", asSystemService=" + asSystemService + + ", useSuggested=" + useSuggested); } // Must use opPackageName for adjusting volumes with UID. final String opPackageName; @@ -685,40 +705,61 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR uid = callingUid; pid = callingPid; } - mHandler.post(new Runnable() { - @Override - public void run() { - try { - if (useSuggested) { - if (AudioSystem.isStreamActive(stream, 0)) { - mAudioManager.adjustSuggestedStreamVolumeForUid(stream, - direction, flags, opPackageName, uid, pid, - mContext.getApplicationInfo().targetSdkVersion); - } else { - mAudioManager.adjustSuggestedStreamVolumeForUid( - AudioManager.USE_DEFAULT_STREAM_TYPE, direction, - flags | previousFlagPlaySound, opPackageName, uid, pid, - mContext.getApplicationInfo().targetSdkVersion); + mHandler.post( + new Runnable() { + @Override + public void run() { + try { + if (useSuggested) { + if (AudioSystem.isStreamActive(stream, 0)) { + mAudioManager.adjustSuggestedStreamVolumeForUid( + stream, + direction, + flags, + opPackageName, + uid, + pid, + mContext.getApplicationInfo().targetSdkVersion); + } else { + mAudioManager.adjustSuggestedStreamVolumeForUid( + AudioManager.USE_DEFAULT_STREAM_TYPE, + direction, + flags | previousFlagPlaySound, + opPackageName, + uid, + pid, + mContext.getApplicationInfo().targetSdkVersion); + } + } else { + mAudioManager.adjustStreamVolumeForUid( + stream, + direction, + flags, + opPackageName, + uid, + pid, + mContext.getApplicationInfo().targetSdkVersion); + } + } catch (IllegalArgumentException | SecurityException e) { + Slog.e( + TAG, + "Cannot adjust volume: direction=" + direction + + ", stream=" + stream + ", flags=" + flags + + ", opPackageName=" + opPackageName + ", uid=" + uid + + ", useSuggested=" + useSuggested + + ", previousFlagPlaySound=" + previousFlagPlaySound, + e); } - } else { - mAudioManager.adjustStreamVolumeForUid(stream, direction, flags, - opPackageName, uid, pid, - mContext.getApplicationInfo().targetSdkVersion); } - } catch (IllegalArgumentException | SecurityException e) { - Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream=" - + stream + ", flags=" + flags + ", opPackageName=" + opPackageName - + ", uid=" + uid + ", useSuggested=" + useSuggested - + ", previousFlagPlaySound=" + previousFlagPlaySound, e); - } - } - }); + }); } private void logCallbackException( String msg, ISessionControllerCallbackHolder holder, Exception e) { - Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName - + ", exception=" + e); + Slog.v( + TAG, + msg + ", this=" + this + ", callback package=" + holder.mPackageName + + ", exception=" + e); } private void pushPlaybackStateUpdate() { @@ -1115,7 +1156,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR throw new IllegalArgumentException( "The media button receiver cannot be set to an activity."); } else { - Log.w(TAG, "Ignoring invalid media button receiver targeting an activity."); + Slog.w( + TAG, + "Ignoring invalid media button receiver targeting an activity."); return; } } @@ -1151,7 +1194,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (CompatChanges.isChangeEnabled(THROW_FOR_INVALID_BROADCAST_RECEIVER, uid)) { throw new IllegalArgumentException("Invalid component name: " + receiver); } else { - Log.w( + Slog.w( TAG, "setMediaButtonBroadcastReceiver(): " + "Ignoring invalid component name=" @@ -1290,7 +1333,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (attributes != null) { mAudioAttrs = attributes; } else { - Log.e(TAG, "Received null audio attributes, using existing attributes"); + Slog.e(TAG, "Received null audio attributes, using existing attributes"); } } if (typeChanged) { @@ -1352,7 +1395,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } return true; } catch (RemoteException e) { - Log.e(TAG, "Remote failure in sendMediaRequest.", e); + Slog.e(TAG, "Remote failure in sendMediaRequest.", e); } return false; } @@ -1375,7 +1418,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } return true; } catch (RemoteException e) { - Log.e(TAG, "Remote failure in sendMediaRequest.", e); + Slog.e(TAG, "Remote failure in sendMediaRequest.", e); } return false; } @@ -1388,7 +1431,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onCommand(packageName, pid, uid, command, args, cb); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in sendCommand.", e); + Slog.e(TAG, "Remote failure in sendCommand.", e); } } @@ -1400,7 +1443,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onCustomAction(packageName, pid, uid, action, args); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in sendCustomAction.", e); + Slog.e(TAG, "Remote failure in sendCustomAction.", e); } } @@ -1411,7 +1454,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPrepare(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in prepare.", e); + Slog.e(TAG, "Remote failure in prepare.", e); } } @@ -1423,7 +1466,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in prepareFromMediaId.", e); + Slog.e(TAG, "Remote failure in prepareFromMediaId.", e); } } @@ -1435,7 +1478,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPrepareFromSearch(packageName, pid, uid, query, extras); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in prepareFromSearch.", e); + Slog.e(TAG, "Remote failure in prepareFromSearch.", e); } } @@ -1446,7 +1489,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPrepareFromUri(packageName, pid, uid, uri, extras); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in prepareFromUri.", e); + Slog.e(TAG, "Remote failure in prepareFromUri.", e); } } @@ -1457,7 +1500,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPlay(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in play.", e); + Slog.e(TAG, "Remote failure in play.", e); } } @@ -1469,7 +1512,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in playFromMediaId.", e); + Slog.e(TAG, "Remote failure in playFromMediaId.", e); } } @@ -1481,7 +1524,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPlayFromSearch(packageName, pid, uid, query, extras); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in playFromSearch.", e); + Slog.e(TAG, "Remote failure in playFromSearch.", e); } } @@ -1492,7 +1535,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPlayFromUri(packageName, pid, uid, uri, extras); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in playFromUri.", e); + Slog.e(TAG, "Remote failure in playFromUri.", e); } } @@ -1503,7 +1546,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onSkipToTrack(packageName, pid, uid, id); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in skipToTrack", e); + Slog.e(TAG, "Remote failure in skipToTrack", e); } } @@ -1514,7 +1557,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPause(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in pause.", e); + Slog.e(TAG, "Remote failure in pause.", e); } } @@ -1525,7 +1568,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onStop(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in stop.", e); + Slog.e(TAG, "Remote failure in stop.", e); } } @@ -1536,7 +1579,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onNext(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in next.", e); + Slog.e(TAG, "Remote failure in next.", e); } } @@ -1547,7 +1590,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onPrevious(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in previous.", e); + Slog.e(TAG, "Remote failure in previous.", e); } } @@ -1558,7 +1601,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onFastForward(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in fastForward.", e); + Slog.e(TAG, "Remote failure in fastForward.", e); } } @@ -1569,7 +1612,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onRewind(packageName, pid, uid); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in rewind.", e); + Slog.e(TAG, "Remote failure in rewind.", e); } } @@ -1580,7 +1623,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onSeekTo(packageName, pid, uid, pos); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in seekTo.", e); + Slog.e(TAG, "Remote failure in seekTo.", e); } } @@ -1591,7 +1634,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onRate(packageName, pid, uid, rating); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in rate.", e); + Slog.e(TAG, "Remote failure in rate.", e); } } @@ -1602,7 +1645,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onSetPlaybackSpeed(packageName, pid, uid, speed); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in setPlaybackSpeed.", e); + Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e); } } @@ -1619,7 +1662,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR mCb.onAdjustVolume(packageName, pid, uid, direction); } } catch (RemoteException e) { - Log.e(TAG, "Remote failure in adjustVolume.", e); + Slog.e(TAG, "Remote failure in adjustVolume.", e); } } @@ -1630,7 +1673,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pid, uid, packageName, reason); mCb.onSetVolumeTo(packageName, pid, uid, value); } catch (RemoteException e) { - Log.e(TAG, "Remote failure in setVolumeTo.", e); + Slog.e(TAG, "Remote failure in setVolumeTo.", e); } } @@ -1673,8 +1716,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR cb, packageName, Binder.getCallingUid(), () -> unregisterCallback(cb)); mControllerCallbackHolders.add(holder); if (DEBUG) { - Log.d(TAG, "registering controller callback " + cb + " from controller" - + packageName); + Slog.d( + TAG, + "registering controller callback " + cb + + " from controller" + packageName); } // Avoid callback leaks try { @@ -1683,7 +1728,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR cb.asBinder().linkToDeath(holder.mDeathMonitor, 0); } catch (RemoteException e) { unregisterCallback(cb); - Log.w(TAG, "registerCallback failed to linkToDeath", e); + Slog.w(TAG, "registerCallback failed to linkToDeath", e); } } } @@ -1698,12 +1743,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR cb.asBinder().unlinkToDeath( mControllerCallbackHolders.get(index).mDeathMonitor, 0); } catch (NoSuchElementException e) { - Log.w(TAG, "error unlinking to binder death", e); + Slog.w(TAG, "error unlinking to binder death", e); } mControllerCallbackHolders.remove(index); } if (DEBUG) { - Log.d(TAG, "unregistering callback " + cb.asBinder()); + Slog.d(TAG, "unregistering callback " + cb.asBinder()); } } } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 78077a831622..c8dba800a017 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -228,8 +228,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { return; } - MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); - if (TextUtils.equals(routeId, deviceRoute.getId())) { + MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute(); + if (TextUtils.equals(routeId, selectedDeviceRoute.getId())) { mBluetoothRouteController.transferTo(null); } else { mBluetoothRouteController.transferTo(routeId); @@ -278,11 +278,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { return null; } - MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); + MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute(); RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, packageName).setSystemSession(true); - builder.addSelectedRoute(deviceRoute.getId()); + builder.addSelectedRoute(selectedDeviceRoute.getId()); for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) { builder.addTransferableRoute(route.getId()); } @@ -314,7 +314,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); // We must have a device route in the provider info. - builder.addRoute(mDeviceRouteController.getDeviceRoute()); + builder.addRoute(mDeviceRouteController.getSelectedRoute()); for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) { builder.addRoute(route); @@ -338,12 +338,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SYSTEM_SESSION_ID, "" /* clientPackageName */) .setSystemSession(true); - MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); - MediaRoute2Info selectedRoute = deviceRoute; + MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute(); + MediaRoute2Info selectedRoute = selectedDeviceRoute; MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute(); if (selectedBtRoute != null) { selectedRoute = selectedBtRoute; - builder.addTransferableRoute(deviceRoute.getId()); + builder.addTransferableRoute(selectedDeviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); mDefaultRoute = diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bae06347d8a2..c2b59644ce1c 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -184,6 +184,7 @@ import android.companion.AssociationRequest; import android.companion.ICompanionDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; +import android.compat.annotation.EnabledSince; import android.compat.annotation.LoggingOnly; import android.content.AttributionSource; import android.content.BroadcastReceiver; @@ -555,7 +556,7 @@ public class NotificationManagerService extends SystemService { * creation and activation of an implicit {@link android.app.AutomaticZenRule}. */ @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) static final long MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES = 308670109L; private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30); @@ -828,6 +829,22 @@ public class NotificationManagerService extends SystemService { } } + // Removes all notifications with the specified user & package. + public void removePackageNotifications(String pkg, @UserIdInt int userId) { + synchronized (mBufferLock) { + Iterator<Pair<StatusBarNotification, Integer>> bufferIter = descendingIterator(); + while (bufferIter.hasNext()) { + final Pair<StatusBarNotification, Integer> pair = bufferIter.next(); + if (pair.first != null + && userId == pair.first.getNormalizedUserId() + && pkg != null && pkg.equals(pair.first.getPackageName()) + && pair.first.getNotification() != null) { + bufferIter.remove(); + } + } + } + } + void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) { synchronized (mBufferLock) { Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator(); @@ -1902,7 +1919,6 @@ public class NotificationManagerService extends SystemService { unhideNotificationsForPackages(pkgList, uidList); } } - mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList); } } @@ -4021,11 +4037,8 @@ public class NotificationManagerService extends SystemService { Slog.e(TAG, "Failed to getApplicationInfo() in canUseFullScreenIntent()", e); return false; } - final boolean showStickyHunIfDenied = mFlagResolver.isEnabled( - SystemUiSystemPropertiesFlags.NotificationFlags - .SHOW_STICKY_HUN_FOR_DENIED_FSI); return checkUseFullScreenIntentPermission(attributionSource, applicationInfo, - showStickyHunIfDenied /* isAppOpPermission */, false /* forDataDelivery */); + false /* forDataDelivery */); } @Override @@ -4219,7 +4232,8 @@ public class NotificationManagerService extends SystemService { boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel( pkg, callingUid, channelId, callingUid, isSystemOrSystemUi); if (previouslyExisted) { - // Remove from both recent notification archive and notification history + // Remove from both recent notification archive (recently dismissed notifications) + // and notification history mArchive.removeChannelNotifications(pkg, callingUser, channelId); mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId); mListeners.notifyNotificationChannelChanged(pkg, @@ -7274,28 +7288,12 @@ public class NotificationManagerService extends SystemService { notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED; if (notification.fullScreenIntent != null) { - final boolean forceDemoteFsiToStickyHun = mFlagResolver.isEnabled( - SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE); - if (forceDemoteFsiToStickyHun) { + final AttributionSource attributionSource = + new AttributionSource.Builder(notificationUid).setPackageName(pkg).build(); + final boolean canUseFullScreenIntent = checkUseFullScreenIntentPermission( + attributionSource, ai, true /* forDataDelivery */); + if (!canUseFullScreenIntent) { makeStickyHun(notification, pkg, userId); - } else { - final AttributionSource attributionSource = - new AttributionSource.Builder(notificationUid).setPackageName(pkg).build(); - final boolean showStickyHunIfDenied = mFlagResolver.isEnabled( - SystemUiSystemPropertiesFlags.NotificationFlags - .SHOW_STICKY_HUN_FOR_DENIED_FSI); - final boolean canUseFullScreenIntent = checkUseFullScreenIntentPermission( - attributionSource, ai, showStickyHunIfDenied /* isAppOpPermission */, - true /* forDataDelivery */); - if (!canUseFullScreenIntent) { - if (showStickyHunIfDenied) { - makeStickyHun(notification, pkg, userId); - } else { - notification.fullScreenIntent = null; - Slog.w(TAG, "Package " + pkg + ": Use of fullScreenIntent requires the" - + "USE_FULL_SCREEN_INTENT permission"); - } - } } } @@ -7402,27 +7400,20 @@ public class NotificationManagerService extends SystemService { } private boolean checkUseFullScreenIntentPermission(@NonNull AttributionSource attributionSource, - @NonNull ApplicationInfo applicationInfo, boolean isAppOpPermission, + @NonNull ApplicationInfo applicationInfo, boolean forDataDelivery) { if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q) { return true; } - if (isAppOpPermission) { - final int permissionResult; - if (forDataDelivery) { - permissionResult = mPermissionManager.checkPermissionForDataDelivery( - permission.USE_FULL_SCREEN_INTENT, attributionSource, /* message= */ null); - } else { - permissionResult = mPermissionManager.checkPermissionForPreflight( - permission.USE_FULL_SCREEN_INTENT, attributionSource); - } - return permissionResult == PermissionManager.PERMISSION_GRANTED; + final int permissionResult; + if (forDataDelivery) { + permissionResult = mPermissionManager.checkPermissionForDataDelivery( + permission.USE_FULL_SCREEN_INTENT, attributionSource, /* message= */ null); } else { - final int permissionResult = getContext().checkPermission( - permission.USE_FULL_SCREEN_INTENT, attributionSource.getPid(), - attributionSource.getUid()); - return permissionResult == PERMISSION_GRANTED; + permissionResult = mPermissionManager.checkPermissionForPreflight( + permission.USE_FULL_SCREEN_INTENT, attributionSource); } + return permissionResult == PermissionManager.PERMISSION_GRANTED; } private void checkRemoteViews(String pkg, String tag, int id, Notification notification) { @@ -9444,7 +9435,11 @@ public class NotificationManagerService extends SystemService { for (int i = 0; i < size; i++) { final String pkg = pkgList[i]; final int uid = uidList[i]; - mHistoryManager.onPackageRemoved(UserHandle.getUserId(uid), pkg); + final int userHandle = UserHandle.getUserId(uid); + // Removes this package's notifications from both recent notification archive + // (recently dismissed notifications) and notification history. + mArchive.removePackageNotifications(pkg, userHandle); + mHistoryManager.onPackageRemoved(userHandle, pkg); } } if (preferencesChanged) { diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index d2e980b7e355..9a6ea2c2aeb8 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java @@ -530,16 +530,13 @@ interface NotificationRecordLogger { this.timeout_millis = p.r.getSbn().getNotification().getTimeoutAfter(); this.is_non_dismissible = NotificationRecordLogger.isNonDismissible(p.r); - final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver() - .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI); - final boolean hasFullScreenIntent = p.r.getSbn().getNotification().fullScreenIntent != null; final boolean hasFsiRequestedButDeniedFlag = (p.r.getSbn().getNotification().flags & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0; - this.fsi_state = NotificationRecordLogger.getFsiState(isStickyHunFlagEnabled, + this.fsi_state = NotificationRecordLogger.getFsiState( hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType); this.is_locked = p.r.isLocked(); @@ -587,13 +584,10 @@ interface NotificationRecordLogger { * @return FrameworkStatsLog enum of the state of the full screen intent posted with this * notification. */ - static int getFsiState(boolean isStickyHunFlagEnabled, - boolean hasFullScreenIntent, + static int getFsiState(boolean hasFullScreenIntent, boolean hasFsiRequestedButDeniedFlag, NotificationReportedEvent eventType) { - - if (!isStickyHunFlagEnabled - || eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) { + if (eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) { // Zeroes in protos take zero bandwidth, but non-zero numbers take bandwidth, // so we should log 0 when possible. return 0; diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 783e9bbb034f..252664a7e4e4 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -2143,10 +2143,7 @@ public class PreferencesHelper implements RankingConfig { * @return State of the full screen intent permission for this package. */ @VisibleForTesting - int getFsiState(String pkg, int uid, boolean requestedFSIPermission, boolean isFlagEnabled) { - if (!isFlagEnabled) { - return 0; - } + int getFsiState(String pkg, int uid, boolean requestedFSIPermission) { if (!requestedFSIPermission) { return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED; } @@ -2167,10 +2164,8 @@ public class PreferencesHelper implements RankingConfig { * the user. */ @VisibleForTesting - boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags, - boolean isStickyHunFlagEnabled) { - if (!isStickyHunFlagEnabled - || fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) { + boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags) { + if (fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) { return false; } return (currentPermissionFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; @@ -2213,22 +2208,18 @@ public class PreferencesHelper implements RankingConfig { pkgsWithPermissionsToHandle.remove(key); } - final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver() - .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI); - final boolean requestedFSIPermission = mPermissionHelper.hasRequestedPermission( android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, r.uid); - final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission, - isStickyHunFlagEnabled); + final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission); final int currentPermissionFlags = mPm.getPermissionFlags( android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, UserHandle.getUserHandleForUid(r.uid)); final boolean fsiIsUserSet = - isFsiPermissionUserSet(r.pkg, r.uid, fsiState, currentPermissionFlags, - isStickyHunFlagEnabled); + isFsiPermissionUserSet(r.pkg, r.uid, fsiState, + currentPermissionFlags); events.add(FrameworkStatsLog.buildStatsEvent( PACKAGE_NOTIFICATION_PREFERENCES, diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 79cd2a0b236f..92d469ccbfac 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -259,6 +259,19 @@ public interface Computer extends PackageDataSnapshot { */ boolean shouldFilterApplicationIncludingUninstalled(@Nullable PackageStateInternal ps, int callingUid, int userId); + + /** + * Different from + * {@link #shouldFilterApplicationIncludingUninstalled(PackageStateInternal, int, int)}, the + * function returns {@code true} if: + * <ul> + * <li>The target package is not archived. + * <li>The package cannot be found in the device or has been uninstalled in the current user. + * </ul> + */ + boolean shouldFilterApplicationIncludingUninstalledNotArchived( + @Nullable PackageStateInternal ps, + int callingUid, int userId); /** * Different from {@link #shouldFilterApplication(SharedUserSetting, int, int)}, the function * returns {@code true} if packages with the same shared user are all uninstalled in the current diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 11a6d1b8f9a4..e5c4ccc73bc3 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -2455,7 +2455,8 @@ public class ComputerEngine implements Computer { */ public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps, int callingUid, @Nullable ComponentName component, - @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) { + @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall, + boolean filterArchived) { if (Process.isSdkSandboxUid(callingUid)) { int clientAppUid = Process.getAppUidForSdkSandboxUid(callingUid); // SDK sandbox should be able to see it's client app @@ -2469,14 +2470,20 @@ public class ComputerEngine implements Computer { } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; + final boolean packageArchivedForUser = ps != null && PackageArchiver.isArchived( + ps.getUserStateOrDefault(userId)); // Don't treat hiddenUntilInstalled as an uninstalled state, phone app needs to access // these hidden application details to customize carrier apps. Also, allowing the system // caller accessing to application across users. if (ps == null || (filterUninstall - && !isSystemOrRootOrShell(callingUid) - && !ps.isHiddenUntilInstalled() - && !ps.getUserStateOrDefault(userId).isInstalled())) { + && !isSystemOrRootOrShell(callingUid) + && !ps.isHiddenUntilInstalled() + && !ps.getUserStateOrDefault(userId).isInstalled() + // Archived packages behave like uninstalled packages. So if filterUninstall is + // set to true, we dismiss filtering some uninstalled package only if it is + // archived and filterArchived is set as false. + && (!packageArchivedForUser || filterArchived))) { // If caller is instant app or sdk sandbox and ps is null, pretend the application // exists, but, needs to be filtered return (callerIsInstantApp || filterUninstall || Process.isSdkSandboxUid(callingUid)); @@ -2524,7 +2531,20 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) + */ + public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps, + int callingUid, @Nullable ComponentName component, + @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) { + return shouldFilterApplication( + ps, callingUid, component, componentType, userId, filterUninstall, + true /* filterArchived */); + } + + /** + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps, int callingUid, @Nullable ComponentName component, @@ -2534,7 +2554,8 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplication( @Nullable PackageStateInternal ps, int callingUid, int userId) { @@ -2543,7 +2564,8 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplication(@NonNull SharedUserSetting sus, int callingUid, int userId) { @@ -2558,7 +2580,8 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplicationIncludingUninstalled( @Nullable PackageStateInternal ps, int callingUid, int userId) { @@ -2567,7 +2590,19 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) + */ + public final boolean shouldFilterApplicationIncludingUninstalledNotArchived( + @Nullable PackageStateInternal ps, int callingUid, int userId) { + return shouldFilterApplication( + ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */, + false /* filterArchived */); + } + + /** + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplicationIncludingUninstalled( @NonNull SharedUserSetting sus, int callingUid, int userId) { @@ -5015,7 +5050,7 @@ public class ComputerEngine implements Computer { String installerPackageName = installSource.mInstallerPackageName; if (installerPackageName != null) { final PackageStateInternal ps = mSettings.getPackage(installerPackageName); - if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, + if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid, UserHandle.getUserId(callingUid))) { installerPackageName = null; } @@ -5033,7 +5068,8 @@ public class ComputerEngine implements Computer { return InstallSource.EMPTY; } - if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { + if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid, + userId)) { return null; } diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 65c6329587a5..3b3d79e7dee1 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -440,7 +440,7 @@ final class DeletePackageHelper { if (outInfo != null) { // Remember which users are affected, before the installed states are modified outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL) - ? ps.queryInstalledUsers(allUserHandles, /* installed= */true) + ? ps.queryUsersInstalledOrHasData(allUserHandles) : new int[]{userId}; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d38c0a0920b1..47d1df5df1c0 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -3586,21 +3586,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.setDontKillApp(false); } + boolean existingSplitReplacedOrRemoved = false; // Inherit splits if not overridden. if (!ArrayUtils.isEmpty(existing.getSplitNames())) { for (int i = 0; i < existing.getSplitNames().length; i++) { final String splitName = existing.getSplitNames()[i]; final File splitFile = new File(existing.getSplitApkPaths()[i]); final boolean splitRemoved = removeSplitList.contains(splitName); - if (!stagedSplits.contains(splitName) && !splitRemoved) { + final boolean splitReplaced = stagedSplits.contains(splitName); + if (!splitReplaced && !splitRemoved) { inheritFileLocked(splitFile); // Collect the requiredSplitTypes and staged splitTypes from splits CollectionUtils.addAll(requiredSplitTypes, existing.getRequiredSplitTypes()[i]); CollectionUtils.addAll(stagedSplitTypes, existing.getSplitTypes()[i]); + } else { + existingSplitReplacedOrRemoved = true; } } } + if (existingSplitReplacedOrRemoved + && (params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { + // Some splits are being replaced or removed. Make sure the app is restarted. + params.setDontKillApp(false); + } // Inherit compiled oat directory. final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 72090f24a2ed..b50d0a07aa3a 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -779,6 +779,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return readUserState(userId).isInstalled(); } + boolean isArchived(int userId) { + return PackageArchiver.isArchived(readUserState(userId)); + } + int getInstallReason(int userId) { return readUserState(userId).getInstallReason(); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 4e14c908b01b..85563172cf05 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1592,6 +1592,19 @@ public class UserManagerService extends IUserManager.Stub { */ private void showConfirmCredentialToDisableQuietMode( @UserIdInt int userId, @Nullable IntentSender target) { + if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) { + // TODO (b/308121702) It may be brittle to rely on user states to check profile state + int state; + synchronized (mUserStates) { + state = mUserStates.get(userId, UserState.STATE_NONE); + } + if (state != UserState.STATE_NONE) { + Slog.i(LOG_TAG, + "showConfirmCredentialToDisableQuietMode() called too early, user " + userId + + " is still alive."); + return; + } + } // otherwise, we show a profile challenge to trigger decryption of the user final KeyguardManager km = (KeyguardManager) mContext.getSystemService( Context.KEYGUARD_SERVICE); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 17499bb77239..940feb580a96 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -24,12 +24,14 @@ import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_PASSTHROUGH; import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED; import static android.hardware.graphics.common.Hdr.DOLBY_VISION; +import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkTemplate.MATCH_ETHERNET; import static android.net.NetworkTemplate.MATCH_MOBILE; +import static android.net.NetworkTemplate.MATCH_PROXY; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.OEM_MANAGED_ALL; import static android.net.NetworkTemplate.OEM_MANAGED_PAID; @@ -488,6 +490,7 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: + case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG: case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER: @@ -973,21 +976,33 @@ public class StatsPullAtomService extends SystemService { if (DEBUG) { Slog.d(TAG, "Registering NetworkStats pullers with statsd"); } + + boolean canQueryTypeProxy = canQueryNetworkStatsForTypeProxy(); + // Initialize NetworkStats baselines. - mNetworkStatsBaselines.addAll( - collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER)); - mNetworkStatsBaselines.addAll( - collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG)); - mNetworkStatsBaselines.addAll( - collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER)); - mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom( - FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG)); - mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom( - FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED)); - mNetworkStatsBaselines.addAll( - collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER)); - mNetworkStatsBaselines.addAll( - collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER)); + synchronized (mDataBytesTransferLock) { + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom( + FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER)); + mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom( + FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG)); + mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom( + FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom( + FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom( + FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER)); + if (canQueryTypeProxy) { + mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom( + FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG)); + } + } // Listen to subscription changes to record historical subscriptions that activated before // pulling, this is used by {@code DATA_USAGE_BYTES_TRANSFER}. @@ -1001,6 +1016,9 @@ public class StatsPullAtomService extends SystemService { registerBytesTransferByTagAndMetered(); registerDataUsageBytesTransfer(); registerOemManagedBytesTransfer(); + if (canQueryTypeProxy) { + registerProxyBytesTransferBackground(); + } } private void initAndRegisterDeferredPullers() { @@ -1171,6 +1189,18 @@ public class StatsPullAtomService extends SystemService { } break; } + case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG: { + final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate( + new NetworkTemplate.Builder(MATCH_PROXY).build(), /*includeTags=*/true); + if (stats != null) { + ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats), + new int[]{TRANSPORT_BLUETOOTH}, + /*slicedByFgbg=*/true, /*slicedByTag=*/false, + /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN, + /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/true)); + } + break; + } case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: { final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate( new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true); @@ -1183,7 +1213,7 @@ public class StatsPullAtomService extends SystemService { new int[]{TRANSPORT_WIFI, TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false, /*slicedByTag=*/true, /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN, - /*subInfo=*/null, OEM_MANAGED_ALL)); + /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/false)); } break; } @@ -1225,7 +1255,7 @@ public class StatsPullAtomService extends SystemService { final NetworkStatsExt diff = new NetworkStatsExt( removeEmptyEntries(item.stats.subtract(baseline.stats)), item.transports, item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType, - item.subInfo, item.oemManaged); + item.subInfo, item.oemManaged, item.isTypeProxy); // If no diff, skip. if (!diff.stats.iterator().hasNext()) continue; @@ -1363,7 +1393,7 @@ public class StatsPullAtomService extends SystemService { ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats), new int[]{transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false, /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN, - /*subInfo=*/null, oemManaged)); + /*subInfo=*/null, oemManaged, /*isTypeProxy=*/false)); } } } @@ -1392,6 +1422,21 @@ public class StatsPullAtomService extends SystemService { } /** + * Check if it is possible to query NetworkStats for TYPE_PROXY. This should only be possible + * if the build includes r.android.com/2828315 + * @return true if querying for TYPE_PROXY is allowed + */ + private static boolean canQueryNetworkStatsForTypeProxy() { + try { + new NetworkTemplate.Builder(MATCH_PROXY).build(); + return true; + } catch (IllegalArgumentException e) { + Slog.w(TAG, "Querying network stats for TYPE_PROXY is not allowed"); + return false; + } + } + + /** * Create a snapshot of NetworkStats since boot for the given template, but add 1 bucket * duration before boot as a buffer to ensure at least one full bucket will be included. * Note that this should be only used to calculate diff since the snapshot might contains @@ -1450,7 +1495,7 @@ public class StatsPullAtomService extends SystemService { ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats), new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true, /*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo, - OEM_MANAGED_ALL)); + OEM_MANAGED_ALL, /*isTypeProxy=*/false)); } } return ret; @@ -1600,6 +1645,19 @@ public class StatsPullAtomService extends SystemService { ); } + private void registerProxyBytesTransferBackground() { + int tagId = FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[]{3, 4, 5, 6}) + .build(); + mStatsManager.setPullAtomCallback( + tagId, + metadata, + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + private void registerBytesTransferByTagAndMetered() { int tagId = FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED; PullAtomMetadata metadata = new PullAtomMetadata.Builder() diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java index 7dbba0d4337d..512f0bf7ff98 100644 --- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java +++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java @@ -42,15 +42,17 @@ public class NetworkStatsExt { public final int oemManaged; @Nullable public final SubInfo subInfo; + public final boolean isTypeProxy; // True if matching ConnectivityManager#TYPE_PROXY public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg) { this(stats, transports, slicedByFgbg, /*slicedByTag=*/false, /*slicedByMetered=*/false, - TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null, OEM_MANAGED_ALL); + TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null, + OEM_MANAGED_ALL, /*isTypeProxy=*/false); } public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg, boolean slicedByTag, boolean slicedByMetered, int ratType, - @Nullable SubInfo subInfo, int oemManaged) { + @Nullable SubInfo subInfo, int oemManaged, boolean isTypeProxy) { this.stats = stats; // Sort transports array so that we can test for equality without considering order. @@ -63,6 +65,7 @@ public class NetworkStatsExt { this.ratType = ratType; this.subInfo = subInfo; this.oemManaged = oemManaged; + this.isTypeProxy = isTypeProxy; } /** @@ -72,6 +75,6 @@ public class NetworkStatsExt { return Arrays.equals(transports, other.transports) && slicedByFgbg == other.slicedByFgbg && slicedByTag == other.slicedByTag && slicedByMetered == other.slicedByMetered && ratType == other.ratType && Objects.equals(subInfo, other.subInfo) - && oemManaged == other.oemManaged; + && oemManaged == other.oemManaged && isTypeProxy == other.isTypeProxy; } } diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index ee46ce13ee73..b3672ecb194c 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -16,6 +16,8 @@ package com.android.server.webkit; +import static android.webkit.Flags.updateServiceV2; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -51,7 +53,7 @@ public class WebViewUpdateService extends SystemService { private static final String TAG = "WebViewUpdateService"; private BroadcastReceiver mWebViewUpdatedReceiver; - private WebViewUpdateServiceImpl mImpl; + private WebViewUpdateServiceInterface mImpl; static final int PACKAGE_CHANGED = 0; static final int PACKAGE_ADDED = 1; @@ -60,7 +62,11 @@ public class WebViewUpdateService extends SystemService { public WebViewUpdateService(Context context) { super(context); - mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance()); + if (updateServiceV2()) { + mImpl = new WebViewUpdateServiceImpl2(context, SystemImpl.getInstance()); + } else { + mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance()); + } } @Override diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java index 43d62aaa120a..cfdef1471f83 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java @@ -63,7 +63,7 @@ import java.util.List; * * @hide */ -class WebViewUpdateServiceImpl { +class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface { private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName(); private static class WebViewPackageMissingException extends Exception { @@ -112,7 +112,8 @@ class WebViewUpdateServiceImpl { mSystemInterface = systemInterface; } - void packageStateChanged(String packageName, int changedState, int userId) { + @Override + public void packageStateChanged(String packageName, int changedState, int userId) { // We don't early out here in different cases where we could potentially early-out (e.g. if // we receive PACKAGE_CHANGED for another user than the system user) since that would // complicate this logic further and open up for more edge cases. @@ -163,7 +164,8 @@ class WebViewUpdateServiceImpl { } } - void prepareWebViewInSystemServer() { + @Override + public void prepareWebViewInSystemServer() { mSystemInterface.notifyZygote(isMultiProcessEnabled()); try { synchronized (mLock) { @@ -210,7 +212,8 @@ class WebViewUpdateServiceImpl { mSystemInterface.ensureZygoteStarted(); } - void handleNewUser(int userId) { + @Override + public void handleNewUser(int userId) { // The system user is always started at boot, and by that point we have already run one // round of the package-changing logic (through prepareWebViewInSystemServer()), so early // out here. @@ -218,7 +221,8 @@ class WebViewUpdateServiceImpl { handleUserChange(); } - void handleUserRemoved(int userId) { + @Override + public void handleUserRemoved(int userId) { handleUserChange(); } @@ -232,14 +236,16 @@ class WebViewUpdateServiceImpl { updateCurrentWebViewPackage(null); } - void notifyRelroCreationCompleted() { + @Override + public void notifyRelroCreationCompleted() { synchronized (mLock) { mNumRelroCreationsFinished++; checkIfRelrosDoneLocked(); } } - WebViewProviderResponse waitForAndGetProvider() { + @Override + public WebViewProviderResponse waitForAndGetProvider() { PackageInfo webViewPackage = null; final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; boolean webViewReady = false; @@ -284,7 +290,8 @@ class WebViewUpdateServiceImpl { * replacing that provider it will not be in use directly, but will be used when the relros * or the replacement are done). */ - String changeProviderAndSetting(String newProviderName) { + @Override + public String changeProviderAndSetting(String newProviderName) { PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName); if (newPackage == null) return ""; return newPackage.packageName; @@ -367,7 +374,8 @@ class WebViewUpdateServiceImpl { /** * Fetch only the currently valid WebView packages. **/ - WebViewProviderInfo[] getValidWebViewPackages() { + @Override + public WebViewProviderInfo[] getValidWebViewPackages() { ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); WebViewProviderInfo[] providers = new WebViewProviderInfo[providersAndPackageInfos.length]; @@ -464,11 +472,13 @@ class WebViewUpdateServiceImpl { return true; } - WebViewProviderInfo[] getWebViewPackages() { + @Override + public WebViewProviderInfo[] getWebViewPackages() { return mSystemInterface.getWebViewPackages(); } - PackageInfo getCurrentWebViewPackage() { + @Override + public PackageInfo getCurrentWebViewPackage() { synchronized (mLock) { return mCurrentWebViewPackage; } @@ -620,7 +630,8 @@ class WebViewUpdateServiceImpl { return null; } - boolean isMultiProcessEnabled() { + @Override + public boolean isMultiProcessEnabled() { int settingValue = mSystemInterface.getMultiProcessSetting(mContext); if (mSystemInterface.isMultiProcessDefaultEnabled()) { // Multiprocess should be enabled unless the user has turned it off manually. @@ -631,7 +642,8 @@ class WebViewUpdateServiceImpl { } } - void enableMultiProcess(boolean enable) { + @Override + public void enableMultiProcess(boolean enable) { PackageInfo current = getCurrentWebViewPackage(); mSystemInterface.setMultiProcessSetting(mContext, enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); @@ -644,7 +656,8 @@ class WebViewUpdateServiceImpl { /** * Dump the state of this Service. */ - void dumpState(PrintWriter pw) { + @Override + public void dumpState(PrintWriter pw) { pw.println("Current WebView Update Service state"); pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); synchronized (mLock) { diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java new file mode 100644 index 000000000000..e618c7e2a80c --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java @@ -0,0 +1,747 @@ +/* + * 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.server.webkit; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.os.AsyncTask; +import android.os.Trace; +import android.os.UserHandle; +import android.util.Slog; +import android.webkit.UserPackage; +import android.webkit.WebViewFactory; +import android.webkit.WebViewProviderInfo; +import android.webkit.WebViewProviderResponse; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of the WebViewUpdateService. + * This class doesn't depend on the android system like the actual Service does and can be used + * directly by tests (as long as they implement a SystemInterface). + * + * This class keeps track of and prepares the current WebView implementation, and needs to keep + * track of a couple of different things such as what package is used as WebView implementation. + * + * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI + * thread or on one of multiple Binder threads. The WebView preparation code shares state between + * threads meaning that code that chooses a new WebView implementation or checks which + * implementation is being used needs to hold a lock. + * + * The WebViewUpdateService can be accessed in a couple of different ways. + * 1. It is started from the SystemServer at boot - at that point we just initiate some state such + * as the WebView preparation class. + * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot + * and the WebViewUpdateService should not have been accessed before this call. In this call we + * choose WebView implementation for the first time. + * 3. The update service listens for Intents related to package installs and removals. These intents + * are received and processed on the UI thread. Each intent can result in changing WebView + * implementation. + * 4. The update service can be reached through Binder calls which are handled on specific binder + * threads. These calls can be made from any process. Generally they are used for changing WebView + * implementation (from Settings), getting information about the current WebView implementation (for + * loading WebView into an app process), or notifying the service about Relro creation being + * completed. + * + * @hide + */ +class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { + private static final String TAG = WebViewUpdateServiceImpl2.class.getSimpleName(); + + private static class WebViewPackageMissingException extends Exception { + WebViewPackageMissingException(String message) { + super(message); + } + + WebViewPackageMissingException(Exception e) { + super(e); + } + } + + private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000. + private static final long NS_PER_MS = 1000000; + + private static final int VALIDITY_OK = 0; + private static final int VALIDITY_INCORRECT_SDK_VERSION = 1; + private static final int VALIDITY_INCORRECT_VERSION_CODE = 2; + private static final int VALIDITY_INCORRECT_SIGNATURE = 3; + private static final int VALIDITY_NO_LIBRARY_FLAG = 4; + + private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE; + private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE; + + private final SystemInterface mSystemInterface; + private final Context mContext; + + private long mMinimumVersionCode = -1; + + // Keeps track of the number of running relro creations + private int mNumRelroCreationsStarted = 0; + private int mNumRelroCreationsFinished = 0; + // Implies that we need to rerun relro creation because we are using an out-of-date package + private boolean mWebViewPackageDirty = false; + private boolean mAnyWebViewInstalled = false; + + private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; + + // The WebView package currently in use (or the one we are preparing). + private PackageInfo mCurrentWebViewPackage = null; + + private final Object mLock = new Object(); + + WebViewUpdateServiceImpl2(Context context, SystemInterface systemInterface) { + mContext = context; + mSystemInterface = systemInterface; + } + + @Override + public void packageStateChanged(String packageName, int changedState, int userId) { + // We don't early out here in different cases where we could potentially early-out (e.g. if + // we receive PACKAGE_CHANGED for another user than the system user) since that would + // complicate this logic further and open up for more edge cases. + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + String webviewPackage = provider.packageName; + + if (webviewPackage.equals(packageName)) { + boolean updateWebView = false; + boolean removedOrChangedOldPackage = false; + String oldProviderName = null; + PackageInfo newPackage = null; + synchronized (mLock) { + try { + newPackage = findPreferredWebViewPackage(); + if (mCurrentWebViewPackage != null) { + oldProviderName = mCurrentWebViewPackage.packageName; + } + // Only trigger update actions if the updated package is the one + // that will be used, or the one that was in use before the + // update, or if we haven't seen a valid WebView package before. + updateWebView = + provider.packageName.equals(newPackage.packageName) + || provider.packageName.equals(oldProviderName) + || mCurrentWebViewPackage == null; + // We removed the old package if we received an intent to remove + // or replace the old package. + removedOrChangedOldPackage = + provider.packageName.equals(oldProviderName); + if (updateWebView) { + onWebViewProviderChanged(newPackage); + } + } catch (WebViewPackageMissingException e) { + mCurrentWebViewPackage = null; + Slog.e(TAG, "Could not find valid WebView package to create relro with " + + e); + } + } + if (updateWebView && !removedOrChangedOldPackage + && oldProviderName != null) { + // If the provider change is the result of adding or replacing a + // package that was not the previous provider then we must kill + // packages dependent on the old package ourselves. The framework + // only kills dependents of packages that are being removed. + mSystemInterface.killPackageDependents(oldProviderName); + } + return; + } + } + } + + @Override + public void prepareWebViewInSystemServer() { + mSystemInterface.notifyZygote(isMultiProcessEnabled()); + try { + synchronized (mLock) { + mCurrentWebViewPackage = findPreferredWebViewPackage(); + String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext); + if (userSetting != null + && !userSetting.equals(mCurrentWebViewPackage.packageName)) { + // Don't persist the user-chosen setting across boots if the package being + // chosen is not used (could be disabled or uninstalled) so that the user won't + // be surprised by the device switching to using a certain webview package, + // that was uninstalled/disabled a long time ago, if it is installed/enabled + // again. + mSystemInterface.updateUserSetting(mContext, + mCurrentWebViewPackage.packageName); + } + onWebViewProviderChanged(mCurrentWebViewPackage); + } + } catch (Throwable t) { + // Log and discard errors at this stage as we must not crash the system server. + Slog.e(TAG, "error preparing webview provider from system server", t); + } + + if (getCurrentWebViewPackage() == null) { + // We didn't find a valid WebView implementation. Try explicitly re-enabling the + // fallback package for all users in case it was disabled, even if we already did the + // one-time migration before. If this actually changes the state, we will see the + // PackageManager broadcast shortly and try again. + WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); + WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); + if (fallbackProvider != null) { + Slog.w(TAG, "No valid provider, trying to enable " + fallbackProvider.packageName); + mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, + true); + } else { + Slog.e(TAG, "No valid provider and no fallback available."); + } + } + } + + private void startZygoteWhenReady() { + // Wait on a background thread for RELRO creation to be done. We ignore the return value + // because even if RELRO creation failed we still want to start the zygote. + waitForAndGetProvider(); + mSystemInterface.ensureZygoteStarted(); + } + + @Override + public void handleNewUser(int userId) { + // The system user is always started at boot, and by that point we have already run one + // round of the package-changing logic (through prepareWebViewInSystemServer()), so early + // out here. + if (userId == UserHandle.USER_SYSTEM) return; + handleUserChange(); + } + + @Override + public void handleUserRemoved(int userId) { + handleUserChange(); + } + + /** + * Called when a user was added or removed to ensure WebView preparation is triggered. + * This has to be done since the WebView package we use depends on the enabled-state + * of packages for all users (so adding or removing a user might cause us to change package). + */ + private void handleUserChange() { + // Potentially trigger package-changing logic. + updateCurrentWebViewPackage(null); + } + + @Override + public void notifyRelroCreationCompleted() { + synchronized (mLock) { + mNumRelroCreationsFinished++; + checkIfRelrosDoneLocked(); + } + } + + @Override + public WebViewProviderResponse waitForAndGetProvider() { + PackageInfo webViewPackage = null; + final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; + boolean webViewReady = false; + int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; + synchronized (mLock) { + webViewReady = webViewIsReadyLocked(); + while (!webViewReady) { + final long timeNowMs = System.nanoTime() / NS_PER_MS; + if (timeNowMs >= timeoutTimeMs) break; + try { + mLock.wait(timeoutTimeMs - timeNowMs); + } catch (InterruptedException e) { + // ignore + } + webViewReady = webViewIsReadyLocked(); + } + // Make sure we return the provider that was used to create the relro file + webViewPackage = mCurrentWebViewPackage; + if (webViewReady) { + // success + } else if (!mAnyWebViewInstalled) { + webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; + } else { + // Either the current relro creation isn't done yet, or the new relro creatioin + // hasn't kicked off yet (the last relro creation used an out-of-date WebView). + webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; + String timeoutError = "Timed out waiting for relro creation, relros started " + + mNumRelroCreationsStarted + + " relros finished " + mNumRelroCreationsFinished + + " package dirty? " + mWebViewPackageDirty; + Slog.e(TAG, timeoutError); + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, timeoutError); + } + } + if (!webViewReady) Slog.w(TAG, "creating relro file timed out"); + return new WebViewProviderResponse(webViewPackage, webViewStatus); + } + + /** + * Change WebView provider and provider setting and kill packages using the old provider. + * Return the new provider (in case we are in the middle of creating relro files, or + * replacing that provider it will not be in use directly, but will be used when the relros + * or the replacement are done). + */ + @Override + public String changeProviderAndSetting(String newProviderName) { + PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName); + if (newPackage == null) return ""; + return newPackage.packageName; + } + + /** + * Update the current WebView package. + * @param newProviderName the package to switch to, null if no package has been explicitly + * chosen. + */ + private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) { + PackageInfo oldPackage = null; + PackageInfo newPackage = null; + boolean providerChanged = false; + synchronized (mLock) { + oldPackage = mCurrentWebViewPackage; + + if (newProviderName != null) { + mSystemInterface.updateUserSetting(mContext, newProviderName); + } + + try { + newPackage = findPreferredWebViewPackage(); + providerChanged = (oldPackage == null) + || !newPackage.packageName.equals(oldPackage.packageName); + } catch (WebViewPackageMissingException e) { + // If updated the Setting but don't have an installed WebView package, the + // Setting will be used when a package is available. + mCurrentWebViewPackage = null; + Slog.e(TAG, "Couldn't find WebView package to use " + e); + return null; + } + // Perform the provider change if we chose a new provider + if (providerChanged) { + onWebViewProviderChanged(newPackage); + } + } + // Kill apps using the old provider only if we changed provider + if (providerChanged && oldPackage != null) { + mSystemInterface.killPackageDependents(oldPackage.packageName); + } + // Return the new provider, this is not necessarily the one we were asked to switch to, + // but the persistent setting will now be pointing to the provider we were asked to + // switch to anyway. + return newPackage; + } + + /** + * This is called when we change WebView provider, either when the current provider is + * updated or a new provider is chosen / takes precedence. + */ + private void onWebViewProviderChanged(PackageInfo newPackage) { + synchronized (mLock) { + mAnyWebViewInstalled = true; + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + mCurrentWebViewPackage = newPackage; + + // The relro creations might 'finish' (not start at all) before + // WebViewFactory.onWebViewProviderChanged which means we might not know the + // number of started creations before they finish. + mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; + mNumRelroCreationsFinished = 0; + mNumRelroCreationsStarted = + mSystemInterface.onWebViewProviderChanged(newPackage); + // If the relro creations finish before we know the number of started creations + // we will have to do any cleanup/notifying here. + checkIfRelrosDoneLocked(); + } else { + mWebViewPackageDirty = true; + } + } + + // Once we've notified the system that the provider has changed and started RELRO creation, + // try to restart the zygote so that it will be ready when apps use it. + if (isMultiProcessEnabled()) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady); + } + } + + /** + * Fetch only the currently valid WebView packages. + **/ + @Override + public WebViewProviderInfo[] getValidWebViewPackages() { + ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); + WebViewProviderInfo[] providers = + new WebViewProviderInfo[providersAndPackageInfos.length]; + for (int n = 0; n < providersAndPackageInfos.length; n++) { + providers[n] = providersAndPackageInfos[n].provider; + } + return providers; + } + + private static class ProviderAndPackageInfo { + public final WebViewProviderInfo provider; + public final PackageInfo packageInfo; + + ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) { + this.provider = provider; + this.packageInfo = packageInfo; + } + } + + private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() { + WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); + List<ProviderAndPackageInfo> providers = new ArrayList<>(); + for (int n = 0; n < allProviders.length; n++) { + try { + PackageInfo packageInfo = + mSystemInterface.getPackageInfoForProvider(allProviders[n]); + if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) { + providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo)); + } + } catch (NameNotFoundException e) { + // Don't add non-existent packages + } + } + return providers.toArray(new ProviderAndPackageInfo[providers.size()]); + } + + /** + * Returns either the package info of the WebView provider determined in the following way: + * If the user has chosen a provider then use that if it is valid, + * otherwise use the first package in the webview priority list that is valid. + * + */ + private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException { + ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos(); + + String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext); + + // If the user has chosen provider, use that (if it's installed and enabled for all + // users). + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.packageName.equals(userChosenProvider)) { + // userPackages can contain null objects. + List<UserPackage> userPackages = + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, + providerAndPackage.provider); + if (isInstalledAndEnabledForAllUsers(userPackages)) { + return providerAndPackage.packageInfo; + } + } + } + + // User did not choose, or the choice failed; use the most stable provider that is + // installed and enabled for all users, and available by default (not through + // user choice). + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.availableByDefault) { + // userPackages can contain null objects. + List<UserPackage> userPackages = + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, + providerAndPackage.provider); + if (isInstalledAndEnabledForAllUsers(userPackages)) { + return providerAndPackage.packageInfo; + } + } + } + + // This should never happen during normal operation (only with modified system images). + mAnyWebViewInstalled = false; + throw new WebViewPackageMissingException("Could not find a loadable WebView package"); + } + + /** + * Return true iff {@param packageInfos} point to only installed and enabled packages. + * The given packages {@param packageInfos} should all be pointing to the same package, but each + * PackageInfo representing a different user's package. + */ + private static boolean isInstalledAndEnabledForAllUsers( + List<UserPackage> userPackages) { + for (UserPackage userPackage : userPackages) { + if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) { + return false; + } + } + return true; + } + + @Override + public WebViewProviderInfo[] getWebViewPackages() { + return mSystemInterface.getWebViewPackages(); + } + + @Override + public PackageInfo getCurrentWebViewPackage() { + synchronized (mLock) { + return mCurrentWebViewPackage; + } + } + + /** + * Returns whether WebView is ready and is not going to go through its preparation phase + * again directly. + */ + private boolean webViewIsReadyLocked() { + return !mWebViewPackageDirty + && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) + // The current package might be replaced though we haven't received an intent + // declaring this yet, the following flag makes anyone loading WebView to wait in + // this case. + && mAnyWebViewInstalled; + } + + private void checkIfRelrosDoneLocked() { + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + if (mWebViewPackageDirty) { + mWebViewPackageDirty = false; + // If we have changed provider since we started the relro creation we need to + // redo the whole process using the new package instead. + try { + PackageInfo newPackage = findPreferredWebViewPackage(); + onWebViewProviderChanged(newPackage); + } catch (WebViewPackageMissingException e) { + mCurrentWebViewPackage = null; + // If we can't find any valid WebView package we are now in a state where + // mAnyWebViewInstalled is false, so loading WebView will be blocked and we + // should simply wait until we receive an intent declaring a new package was + // installed. + } + } else { + mLock.notifyAll(); + } + } + } + + private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) { + // Ensure the provider targets this framework release (or a later one). + if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) { + return VALIDITY_INCORRECT_SDK_VERSION; + } + if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode()) + && !mSystemInterface.systemIsDebuggable()) { + // Webview providers may be downgraded arbitrarily low, prevent that by enforcing + // minimum version code. This check is only enforced for user builds. + return VALIDITY_INCORRECT_VERSION_CODE; + } + if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) { + return VALIDITY_INCORRECT_SIGNATURE; + } + if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) { + return VALIDITY_NO_LIBRARY_FLAG; + } + return VALIDITY_OK; + } + + /** + * Both versionCodes should be from a WebView provider package implemented by Chromium. + * VersionCodes from other kinds of packages won't make any sense in this method. + * + * An introduction to Chromium versionCode scheme: + * "BBBBPPPXX" + * BBBB: 4 digit branch number. It monotonically increases over time. + * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits + * may change their meaning in the future. + * XX: Digits to differentiate different APK builds of the same source version. + * + * This method takes the "BBBB" of versionCodes and compare them. + * + * https://www.chromium.org/developers/version-numbers describes general Chromium versioning; + * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py + * is the canonical source for how Chromium versionCodes are calculated. + * + * @return true if versionCode1 is higher than or equal to versionCode2. + */ + private static boolean versionCodeGE(long versionCode1, long versionCode2) { + long v1 = versionCode1 / 100000; + long v2 = versionCode2 / 100000; + + return v1 >= v2; + } + + /** + * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode + * of all available-by-default WebView provider packages. If there is no such WebView provider + * package on the system, then return -1, which means all positive versionCode WebView packages + * are accepted. + * + * Note that this is a private method that handles a variable (mMinimumVersionCode) which is + * shared between threads. Furthermore, this method does not hold mLock meaning that we must + * take extra care to ensure this method is thread-safe. + */ + private long getMinimumVersionCode() { + if (mMinimumVersionCode > 0) { + return mMinimumVersionCode; + } + + long minimumVersionCode = -1; + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + if (provider.availableByDefault) { + try { + long versionCode = + mSystemInterface.getFactoryPackageVersion(provider.packageName); + if (minimumVersionCode < 0 || versionCode < minimumVersionCode) { + minimumVersionCode = versionCode; + } + } catch (NameNotFoundException e) { + // Safe to ignore. + } + } + } + + mMinimumVersionCode = minimumVersionCode; + return mMinimumVersionCode; + } + + private static boolean providerHasValidSignature(WebViewProviderInfo provider, + PackageInfo packageInfo, SystemInterface systemInterface) { + // Skip checking signatures on debuggable builds, for development purposes. + if (systemInterface.systemIsDebuggable()) return true; + + // Allow system apps to be valid providers regardless of signature. + if (packageInfo.applicationInfo.isSystemApp()) return true; + + // We don't support packages with multiple signatures. + if (packageInfo.signatures.length != 1) return false; + + // If any of the declared signatures match the package signature, it's valid. + for (Signature signature : provider.signatures) { + if (signature.equals(packageInfo.signatures[0])) return true; + } + + return false; + } + + /** + * Returns the only fallback provider in the set of given packages, or null if there is none. + */ + private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { + for (WebViewProviderInfo provider : webviewPackages) { + if (provider.isFallback) { + return provider; + } + } + return null; + } + + @Override + public boolean isMultiProcessEnabled() { + int settingValue = mSystemInterface.getMultiProcessSetting(mContext); + if (mSystemInterface.isMultiProcessDefaultEnabled()) { + // Multiprocess should be enabled unless the user has turned it off manually. + return settingValue > MULTIPROCESS_SETTING_OFF_VALUE; + } else { + // Multiprocess should not be enabled, unless the user has turned it on manually. + return settingValue >= MULTIPROCESS_SETTING_ON_VALUE; + } + } + + @Override + public void enableMultiProcess(boolean enable) { + PackageInfo current = getCurrentWebViewPackage(); + mSystemInterface.setMultiProcessSetting(mContext, + enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); + mSystemInterface.notifyZygote(enable); + if (current != null) { + mSystemInterface.killPackageDependents(current.packageName); + } + } + + /** + * Dump the state of this Service. + */ + @Override + public void dumpState(PrintWriter pw) { + pw.println("Current WebView Update Service state"); + pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); + synchronized (mLock) { + if (mCurrentWebViewPackage == null) { + pw.println(" Current WebView package is null"); + } else { + pw.println(String.format(" Current WebView package (name, version): (%s, %s)", + mCurrentWebViewPackage.packageName, + mCurrentWebViewPackage.versionName)); + } + pw.println(String.format(" Minimum targetSdkVersion: %d", + UserPackage.MINIMUM_SUPPORTED_SDK)); + pw.println(String.format(" Minimum WebView version code: %d", + mMinimumVersionCode)); + pw.println(String.format(" Number of relros started: %d", + mNumRelroCreationsStarted)); + pw.println(String.format(" Number of relros finished: %d", + mNumRelroCreationsFinished)); + pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty)); + pw.println(String.format(" Any WebView package installed: %b", + mAnyWebViewInstalled)); + + try { + PackageInfo preferredWebViewPackage = findPreferredWebViewPackage(); + pw.println(String.format( + " Preferred WebView package (name, version): (%s, %s)", + preferredWebViewPackage.packageName, + preferredWebViewPackage.versionName)); + } catch (WebViewPackageMissingException e) { + pw.println(String.format(" Preferred WebView package: none")); + } + + dumpAllPackageInformationLocked(pw); + } + } + + private void dumpAllPackageInformationLocked(PrintWriter pw) { + WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); + pw.println(" WebView packages:"); + for (WebViewProviderInfo provider : allProviders) { + List<UserPackage> userPackages = + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider); + PackageInfo systemUserPackageInfo = + userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo(); + if (systemUserPackageInfo == null) { + pw.println(String.format(" %s is NOT installed.", provider.packageName)); + continue; + } + + int validity = validityResult(provider, systemUserPackageInfo); + String packageDetails = String.format( + "versionName: %s, versionCode: %d, targetSdkVersion: %d", + systemUserPackageInfo.versionName, + systemUserPackageInfo.getLongVersionCode(), + systemUserPackageInfo.applicationInfo.targetSdkVersion); + if (validity == VALIDITY_OK) { + boolean installedForAllUsers = isInstalledAndEnabledForAllUsers( + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider)); + pw.println(String.format( + " Valid package %s (%s) is %s installed/enabled for all users", + systemUserPackageInfo.packageName, + packageDetails, + installedForAllUsers ? "" : "NOT")); + } else { + pw.println(String.format(" Invalid package %s (%s), reason: %s", + systemUserPackageInfo.packageName, + packageDetails, + getInvalidityReason(validity))); + } + } + } + + private static String getInvalidityReason(int invalidityReason) { + switch (invalidityReason) { + case VALIDITY_INCORRECT_SDK_VERSION: + return "SDK version too low"; + case VALIDITY_INCORRECT_VERSION_CODE: + return "Version code too low"; + case VALIDITY_INCORRECT_SIGNATURE: + return "Incorrect signature"; + case VALIDITY_NO_LIBRARY_FLAG: + return "No WebView-library manifest flag"; + default: + return "Unexcepted validity-reason"; + } + } +} diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java new file mode 100644 index 000000000000..a9c3dc45842f --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 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.webkit; + +import android.content.pm.PackageInfo; +import android.webkit.WebViewProviderInfo; +import android.webkit.WebViewProviderResponse; + +import java.io.PrintWriter; + +interface WebViewUpdateServiceInterface { + void packageStateChanged(String packageName, int changedState, int userId); + + void handleNewUser(int userId); + + void handleUserRemoved(int userId); + + WebViewProviderInfo[] getWebViewPackages(); + + void prepareWebViewInSystemServer(); + + void notifyRelroCreationCompleted(); + + WebViewProviderResponse waitForAndGetProvider(); + + String changeProviderAndSetting(String newProviderName); + + WebViewProviderInfo[] getValidWebViewPackages(); + + PackageInfo getCurrentWebViewPackage(); + + boolean isMultiProcessEnabled(); + + void enableMultiProcess(boolean enable); + + void dumpState(PrintWriter pw); +} diff --git a/services/core/java/com/android/server/webkit/flags.aconfig b/services/core/java/com/android/server/webkit/flags.aconfig new file mode 100644 index 000000000000..1411acc4ab84 --- /dev/null +++ b/services/core/java/com/android/server/webkit/flags.aconfig @@ -0,0 +1,9 @@ +package: "android.webkit" + +flag { + name: "update_service_v2" + namespace: "webview" + description: "Using a new version of the WebView update service" + bug: "308907090" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index faccca8981c8..315e7d85df24 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -55,6 +55,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH; import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller; +import static com.android.window.flags.Flags.allowDisableActivityRecordInputSink; import android.Manifest; import android.annotation.ColorInt; @@ -1688,4 +1689,20 @@ class ActivityClientController extends IActivityClientController.Stub { return r.mRequestedLaunchingTaskFragmentToken == taskFragmentToken; } } + + @Override + public void setActivityRecordInputSinkEnabled(IBinder activityToken, boolean enabled) { + if (!allowDisableActivityRecordInputSink()) { + return; + } + + mService.mAmInternal.enforceCallingPermission( + Manifest.permission.INTERNAL_SYSTEM_WINDOW, "setActivityRecordInputSinkEnabled"); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken); + if (r != null) { + r.mActivityRecordInputSinkEnabled = enabled; + } + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 081759d563a5..d90d4ff6bbd6 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -970,6 +970,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mWaitForEnteringPinnedMode; final ActivityRecordInputSink mActivityRecordInputSink; + // System activities with INTERNAL_SYSTEM_WINDOW can disable ActivityRecordInputSink. + boolean mActivityRecordInputSinkEnabled = true; // Activities with this uid are allowed to not create an input sink while being in the same // task and directly above this ActivityRecord. This field is updated whenever a new activity diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java index be7d9b63f779..c61d86355c8b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -86,7 +86,8 @@ class ActivityRecordInputSink { final boolean allowPassthrough = activityBelowInTask != null && ( activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() || activityBelowInTask.isUid(mActivityRecord.getUid())); - if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition()) { + if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition() + || !mActivityRecord.mActivityRecordInputSinkEnabled) { mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE, InputConfig.NOT_TOUCHABLE); } else { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a9f11e488881..34c7eee45f34 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -281,6 +281,7 @@ import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.NeededUriGrants; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wallpaper.WallpaperManagerInternal; +import com.android.wm.shell.Flags; import java.io.BufferedReader; import java.io.File; @@ -316,8 +317,6 @@ import java.util.Set; public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender"; private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM; - private static final String ENABLE_PIP2_IMPLEMENTATION = - "persist.wm.debug.enable_pip2_implementation"; static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK; static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; @@ -7262,6 +7261,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } static boolean isPip2ExperimentEnabled() { - return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false); + return Flags.enablePip2Implementation(); } } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 287aaf9da42a..8cc197c2f3d0 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -46,7 +46,6 @@ import android.app.BackgroundStartPrivileges; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; -import android.compat.annotation.Overridable; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; @@ -88,7 +87,6 @@ public class BackgroundActivityStartController { /** If enabled the creator will not allow BAL on its behalf by default. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - @Overridable private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR = 296478951; public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED = diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java index 28f656e624fb..8b282dd3e7a8 100644 --- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java @@ -58,6 +58,23 @@ class ClientLifecycleManager { } /** + * Similar to {@link #scheduleTransactionItem}, but is called without WM lock. + * + * @see WindowProcessController#setReportedProcState(int) + */ + void scheduleTransactionItemUnlocked(@NonNull IApplicationThread client, + @NonNull ClientTransactionItem transactionItem) throws RemoteException { + // Immediately dispatching to client, and must not access WMS. + final ClientTransaction clientTransaction = ClientTransaction.obtain(client); + if (transactionItem.isActivityLifecycleItem()) { + clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); + } else { + clientTransaction.addCallback(transactionItem); + } + scheduleTransaction(clientTransaction); + } + + /** * Schedules a single transaction item, either a callback or a lifecycle request, delivery to * client application. * @throws RemoteException @@ -65,6 +82,7 @@ class ClientLifecycleManager { */ void scheduleTransactionItem(@NonNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem) throws RemoteException { + // TODO(b/260873529): queue the transaction items. final ClientTransaction clientTransaction = ClientTransaction.obtain(client); if (transactionItem.isActivityLifecycleItem()) { clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); @@ -82,6 +100,7 @@ class ClientLifecycleManager { void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem, @NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException { + // TODO(b/260873529): replace with #scheduleTransactionItem after launch for cleanup. final ClientTransaction clientTransaction = ClientTransaction.obtain(client); clientTransaction.addCallback(transactionItem); clientTransaction.setLifecycleStateRequest(lifecycleItem); diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index d0d7f493b969..e6bbd52807bd 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -101,10 +101,8 @@ class InsetsPolicy { mPolicy = displayContent.getDisplayPolicy(); final Resources r = mPolicy.getContext().getResources(); mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard); - mTransientControlTarget = new ControlTarget( - stateController, displayContent.mWmService.mH, "TransientControlTarget"); - mPermanentControlTarget = new ControlTarget( - stateController, displayContent.mWmService.mH, "PermanentControlTarget"); + mTransientControlTarget = new ControlTarget(displayContent, "TransientControlTarget"); + mPermanentControlTarget = new ControlTarget(displayContent, "PermanentControlTarget"); } /** Updates the target which can control system bars. */ @@ -699,24 +697,35 @@ class InsetsPolicy { } } - private static class ControlTarget implements InsetsControlTarget { + private static class ControlTarget implements InsetsControlTarget, Runnable { + private final Handler mHandler; + private final Object mGlobalLock; private final InsetsState mState = new InsetsState(); - private final InsetsController mInsetsController; private final InsetsStateController mStateController; + private final InsetsController mInsetsController; private final String mName; - ControlTarget(InsetsStateController stateController, Handler handler, String name) { - mStateController = stateController; - mInsetsController = new InsetsController(new Host(handler, name)); + ControlTarget(DisplayContent displayContent, String name) { + mHandler = displayContent.mWmService.mH; + mGlobalLock = displayContent.mWmService.mGlobalLock; + mStateController = displayContent.getInsetsStateController(); + mInsetsController = new InsetsController(new Host(mHandler, name)); mName = name; } @Override public void notifyInsetsControlChanged() { - mState.set(mStateController.getRawInsetsState(), true /* copySources */); - mInsetsController.onStateChanged(mState); - mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this)); + mHandler.post(this); + } + + @Override + public void run() { + synchronized (mGlobalLock) { + mState.set(mStateController.getRawInsetsState(), true /* copySources */); + mInsetsController.onStateChanged(mState); + mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this)); + } } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java index b3a36501b7cf..89a70e502415 100644 --- a/services/core/java/com/android/server/wm/WindowManagerFlags.java +++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java @@ -43,8 +43,6 @@ class WindowManagerFlags { /* Start Available Flags */ - final boolean mSyncWindowConfigUpdateFlag = Flags.syncWindowConfigUpdateFlag(); - final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag(); final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync(); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 558bf9d6310a..2b18f0775047 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -388,13 +388,22 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio final IApplicationThread thread = mThread; if (prevProcState >= CACHED_CONFIG_PROC_STATE && repProcState < CACHED_CONFIG_PROC_STATE && thread != null && mHasCachedConfiguration) { - final Configuration config; + final ConfigurationChangeItem configurationChangeItem; synchronized (mLastReportedConfiguration) { - config = new Configuration(mLastReportedConfiguration); + onConfigurationChangePreScheduled(mLastReportedConfiguration); + configurationChangeItem = ConfigurationChangeItem.obtain( + mLastReportedConfiguration, mLastTopActivityDeviceId); } // Schedule immediately to make sure the app component (e.g. receiver, service) can get // the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate). - scheduleConfigurationChange(thread, config); + try { + // No WM lock here. + mAtm.getLifecycleManager().scheduleTransactionItemUnlocked( + thread, configurationChangeItem); + } catch (Exception e) { + Slog.e(TAG_CONFIGURATION, "Failed to schedule ConfigurationChangeItem=" + + configurationChangeItem + " owner=" + mOwner, e); + } } } @@ -1634,11 +1643,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } - scheduleConfigurationChange(thread, config); + onConfigurationChangePreScheduled(config); + scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain( + config, mLastTopActivityDeviceId)); } - private void scheduleConfigurationChange(@NonNull IApplicationThread thread, - @NonNull Configuration config) { + private void onConfigurationChangePreScheduled(@NonNull Configuration config) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, config); if (Build.IS_DEBUGGABLE && mHasImeService) { @@ -1646,8 +1656,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); } mHasCachedConfiguration = false; - scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain( - config, mLastTopActivityDeviceId)); } @VisibleForTesting diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 0d196b48b3f2..7c539502461b 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -2175,9 +2175,9 @@ class PermissionService(private val service: AccessCheckingService) : userState.appIdDevicePermissionFlags[appId]?.forEachIndexed { _, - deviceId, + persistentDeviceId, devicePermissionFlags -> - println("Permissions (Device $deviceId):") + println("Permissions (Device $persistentDeviceId):") withIndent { devicePermissionFlags.forEachIndexed { _, permissionName, flags -> val isGranted = PermissionFlags.isPermissionGranted(flags) @@ -2658,7 +2658,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun onDevicePermissionFlagsChanged( appId: Int, userId: Int, - deviceId: String, + persistentDeviceId: String, permissionName: String, oldFlags: Int, newFlags: Int @@ -2681,7 +2681,8 @@ class PermissionService(private val service: AccessCheckingService) : permissionName in NOTIFICATIONS_PERMISSIONS && runtimePermissionRevokedUids.get(uid, true) } - runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += deviceId + runtimePermissionChangedUidDevices + .getOrPut(uid) { mutableSetOf() } += persistentDeviceId } if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) { @@ -2695,9 +2696,9 @@ class PermissionService(private val service: AccessCheckingService) : isPermissionFlagsChanged = false } - runtimePermissionChangedUidDevices.forEachIndexed { _, uid, deviceIds -> - deviceIds.forEach { deviceId -> - onPermissionsChangeListeners.onPermissionsChanged(uid, deviceId) + runtimePermissionChangedUidDevices.forEachIndexed { _, uid, persistentDeviceIds -> + persistentDeviceIds.forEach { persistentDeviceId -> + onPermissionsChangeListeners.onPermissionsChanged(uid, persistentDeviceId) } } runtimePermissionChangedUidDevices.clear() @@ -2772,16 +2773,16 @@ class PermissionService(private val service: AccessCheckingService) : when (msg.what) { MSG_ON_PERMISSIONS_CHANGED -> { val uid = msg.arg1 - val deviceId = msg.obj as String - handleOnPermissionsChanged(uid, deviceId) + val persistentDeviceId = msg.obj as String + handleOnPermissionsChanged(uid, persistentDeviceId) } } } - private fun handleOnPermissionsChanged(uid: Int, deviceId: String) { + private fun handleOnPermissionsChanged(uid: Int, persistentDeviceId: String) { listeners.broadcast { listener -> try { - listener.onPermissionsChanged(uid, deviceId) + listener.onPermissionsChanged(uid, persistentDeviceId) } catch (e: RemoteException) { Slog.e(LOG_TAG, "Error when calling OnPermissionsChangeListener", e) } @@ -2796,9 +2797,10 @@ class PermissionService(private val service: AccessCheckingService) : listeners.unregister(listener) } - fun onPermissionsChanged(uid: Int, deviceId: String) { + fun onPermissionsChanged(uid: Int, persistentDeviceId: String) { if (listeners.registeredCallbackCount > 0) { - obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget() + obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId) + .sendToTarget() } } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml index 6c24d6d6e5a6..820628c98dee 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml @@ -21,8 +21,8 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> - <option name="test-file-name" value="FrameworksImeTests.apk" /> <option name="test-file-name" value="SimpleTestIme.apk" /> + <option name="test-file-name" value="FrameworksImeTests.apk" /> </target_preparer> <option name="test-tag" value="FrameworksImeTests" /> diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java index a400f1243afb..eb6e8b4469f0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java @@ -44,6 +44,7 @@ public class DisplayBrightnessStateTest { float brightness = 0.3f; float sdrBrightness = 0.2f; boolean shouldUseAutoBrightness = true; + boolean shouldUpdateScreenBrightnessSetting = true; BrightnessReason brightnessReason = new BrightnessReason(); brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC); brightnessReason.setModifier(BrightnessReason.MODIFIER_DIMMED); @@ -52,12 +53,15 @@ public class DisplayBrightnessStateTest { .setSdrBrightness(sdrBrightness) .setBrightnessReason(brightnessReason) .setShouldUseAutoBrightness(shouldUseAutoBrightness) + .setShouldUpdateScreenBrightnessSetting(shouldUpdateScreenBrightnessSetting) .build(); assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA); assertEquals(displayBrightnessState.getSdrBrightness(), sdrBrightness, FLOAT_DELTA); assertEquals(displayBrightnessState.getBrightnessReason(), brightnessReason); assertEquals(displayBrightnessState.getShouldUseAutoBrightness(), shouldUseAutoBrightness); + assertEquals(shouldUpdateScreenBrightnessSetting, + displayBrightnessState.shouldUpdateScreenBrightnessSetting()); assertEquals(displayBrightnessState.toString(), getString(displayBrightnessState)); } @@ -71,6 +75,7 @@ public class DisplayBrightnessStateTest { .setBrightness(0.26f) .setSdrBrightness(0.23f) .setShouldUseAutoBrightness(false) + .setShouldUpdateScreenBrightnessSetting(true) .build(); DisplayBrightnessState state2 = DisplayBrightnessState.Builder.from(state1).build(); assertEquals(state1, state2); @@ -92,7 +97,9 @@ public class DisplayBrightnessStateTest { .append("\n maxBrightness:") .append(displayBrightnessState.getMaxBrightness()) .append("\n customAnimationRate:") - .append(displayBrightnessState.getCustomAnimationRate()); + .append(displayBrightnessState.getCustomAnimationRate()) + .append("\n shouldUpdateScreenBrightnessSetting:") + .append(displayBrightnessState.shouldUpdateScreenBrightnessSetting()); return sb.toString(); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 0bf46547ce1e..353a7bb580ec 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -2754,8 +2754,7 @@ public class DisplayManagerServiceTest { DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class); localService.registerDisplayOffloader(displayId, mockDisplayOffloader); - assertThat(display.getDisplayOffloadSessionLocked().getDisplayOffloader()).isEqualTo( - mockDisplayOffloader); + assertThat(display.getDisplayOffloadSessionLocked()).isNotNull(); } @Test diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java new file mode 100644 index 000000000000..dea838d3763d --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class DisplayOffloadSessionImplTest { + + @Mock + private DisplayManagerInternal.DisplayOffloader mDisplayOffloader; + + @Mock + private DisplayPowerControllerInterface mDisplayPowerController; + + private DisplayOffloadSessionImpl mSession; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mDisplayOffloader.startOffload()).thenReturn(true); + mSession = new DisplayOffloadSessionImpl(mDisplayOffloader, mDisplayPowerController); + } + + @Test + public void testStartOffload() { + mSession.startOffload(); + assertTrue(mSession.isActive()); + + // An active session shouldn't be started again + mSession.startOffload(); + verify(mDisplayOffloader, times(1)).startOffload(); + } + + @Test + public void testStopOffload() { + mSession.startOffload(); + mSession.stopOffload(); + + assertFalse(mSession.isActive()); + verify(mDisplayPowerController).setBrightnessFromOffload( + PowerManager.BRIGHTNESS_INVALID_FLOAT); + + // An inactive session shouldn't be stopped again + mSession.stopOffload(); + verify(mDisplayOffloader, times(1)).stopOffload(); + } + + @Test + public void testUpdateBrightness_sessionInactive() { + mSession.updateBrightness(0.3f); + verify(mDisplayPowerController, never()).setBrightnessFromOffload(anyFloat()); + } + + @Test + public void testUpdateBrightness_sessionActive() { + float brightness = 0.3f; + + mSession.startOffload(); + mSession.updateBrightness(brightness); + + verify(mDisplayPowerController).setBrightnessFromOffload(brightness); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java index 57f392a5887f..693cafefa2c0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java @@ -127,8 +127,6 @@ public final class DisplayPowerController2Test { private Handler mHandler; private DisplayPowerControllerHolder mHolder; private Sensor mProxSensor; - private DisplayManagerInternal.DisplayOffloader mDisplayOffloader; - private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; @Mock private DisplayPowerCallbacks mDisplayPowerCallbacksMock; @@ -148,6 +146,8 @@ public final class DisplayPowerController2Test { private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock; @Mock private DisplayManagerFlags mDisplayManagerFlagsMock; + @Mock + private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; @Captor private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; @@ -1160,19 +1160,19 @@ public final class DisplayPowerController2Test { any(AutomaticBrightnessController.Callbacks.class), any(Looper.class), eq(mSensorManagerMock), - any(), + /* lightSensor= */ any(), eq(mHolder.brightnessMappingStrategy), - anyInt(), - anyFloat(), - anyFloat(), - anyFloat(), - anyInt(), - anyInt(), - anyLong(), - anyLong(), - anyLong(), - anyLong(), - anyBoolean(), + /* lightSensorWarmUpTime= */ anyInt(), + /* brightnessMin= */ anyFloat(), + /* brightnessMax= */ anyFloat(), + /* dozeScaleFactor */ anyFloat(), + /* lightSensorRate= */ anyInt(), + /* initialLightSensorRate= */ anyInt(), + /* brighteningLightDebounceConfig */ anyLong(), + /* darkeningLightDebounceConfig */ anyLong(), + /* brighteningLightDebounceConfigIdle= */ anyLong(), + /* darkeningLightDebounceConfigIdle= */ anyLong(), + /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(), any(HysteresisLevels.class), any(HysteresisLevels.class), any(HysteresisLevels.class), @@ -1180,9 +1180,9 @@ public final class DisplayPowerController2Test { eq(mContext), any(BrightnessRangeController.class), any(BrightnessThrottler.class), - isNull(), - anyInt(), - anyInt(), + /* idleModeBrightnessMapper= */ isNull(), + /* ambientLightHorizonShort= */ anyInt(), + /* ambientLightHorizonLong= */ anyInt(), eq(lux), eq(brightness) ); @@ -1294,9 +1294,8 @@ public final class DisplayPowerController2Test { public void testRampRateForHdrContent_HdrClamperOn() { float clampedBrightness = 0.6f; float transitionRate = 1.5f; - DisplayManagerFlags flags = mock(DisplayManagerFlags.class); - when(flags.isHdrClamperEnabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags); + when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); DisplayPowerRequest dpr = new DisplayPowerRequest(); when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); @@ -1392,9 +1391,8 @@ public final class DisplayPowerController2Test { @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) public void testRampMaxTimeInteractiveThenIdle_DifferentValues() { - DisplayManagerFlags flags = mock(DisplayManagerFlags.class); - when(flags.isAdaptiveTone1Enabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags); + when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); // Send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -1447,9 +1445,8 @@ public final class DisplayPowerController2Test { @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) public void testRampMaxTimeIdle_DifferentValues() { - DisplayManagerFlags flags = mock(DisplayManagerFlags.class); - when(flags.isAdaptiveTone1Enabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags); + when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); // Send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -1481,8 +1478,6 @@ public final class DisplayPowerController2Test { when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); return null; }).when(mHolder.displayPowerState).setScreenState(anyInt()); - // init displayoffload session and support offloading. - initDisplayOffloadSession(); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); // start with DOZE. @@ -1509,8 +1504,6 @@ public final class DisplayPowerController2Test { when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); return null; }).when(mHolder.displayPowerState).setScreenState(anyInt()); - // init displayoffload session and support offloading. - initDisplayOffloadSession(); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); // start with DOZE. @@ -1536,8 +1529,6 @@ public final class DisplayPowerController2Test { when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); return null; }).when(mHolder.displayPowerState).setScreenState(anyInt()); - // init displayoffload session and support offloading. - initDisplayOffloadSession(); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); // start with OFF. @@ -1553,26 +1544,29 @@ public final class DisplayPowerController2Test { verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); } - private void initDisplayOffloadSession() { - mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() { - @Override - public boolean startOffload() { - return true; - } - - @Override - public void stopOffload() {} - }); - - mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() { - @Override - public void setDozeStateOverride(int displayState) {} - - @Override - public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() { - return mDisplayOffloader; - } - }; + @Test + public void testBrightnessFromOffload() { + when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + float brightness = 0.34f; + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); + + mHolder.dpc.setBrightnessFromOffload(brightness); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by handleBrightnessModeChange, another triggered by + // setBrightnessFromOffload + verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); } /** @@ -1659,12 +1653,6 @@ public final class DisplayPowerController2Test { private DisplayPowerControllerHolder createDisplayPowerController(int displayId, String uniqueId, boolean isEnabled) { - return createDisplayPowerController(displayId, uniqueId, isEnabled, - mock(DisplayManagerFlags.class)); - } - - private DisplayPowerControllerHolder createDisplayPowerController(int displayId, - String uniqueId, boolean isEnabled, DisplayManagerFlags flags) { final DisplayPowerState displayPowerState = mock(DisplayPowerState.class); final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class); final AutomaticBrightnessController automaticBrightnessController = @@ -1690,7 +1678,7 @@ public final class DisplayPowerController2Test { TestInjector injector = spy(new TestInjector(displayPowerState, animator, automaticBrightnessController, wakelockController, brightnessMappingStrategy, hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper, - clamperController, flags)); + clamperController, mDisplayManagerFlagsMock)); final LogicalDisplay display = mock(LogicalDisplay.class); final DisplayDevice device = mock(DisplayDevice.class); @@ -1705,7 +1693,7 @@ public final class DisplayPowerController2Test { mSensorManagerMock, mDisplayBlankerMock, display, mBrightnessTrackerMock, brightnessSetting, () -> { }, - hbmMetadata, /* bootCompleted= */ false, flags); + hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock); return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, animator, automaticBrightnessController, wakelockController, diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 9617bd08fd93..b22799377872 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -125,8 +125,6 @@ public final class DisplayPowerControllerTest { private Handler mHandler; private DisplayPowerControllerHolder mHolder; private Sensor mProxSensor; - private DisplayManagerInternal.DisplayOffloader mDisplayOffloader; - private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; @Mock private DisplayPowerCallbacks mDisplayPowerCallbacksMock; @@ -146,6 +144,8 @@ public final class DisplayPowerControllerTest { private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock; @Mock private DisplayManagerFlags mDisplayManagerFlagsMock; + @Mock + private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; @Captor private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; @@ -1094,19 +1094,19 @@ public final class DisplayPowerControllerTest { any(AutomaticBrightnessController.Callbacks.class), any(Looper.class), eq(mSensorManagerMock), - any(), + /* lightSensor= */ any(), eq(mHolder.brightnessMappingStrategy), - anyInt(), - anyFloat(), - anyFloat(), - anyFloat(), - anyInt(), - anyInt(), - anyLong(), - anyLong(), - anyLong(), - anyLong(), - anyBoolean(), + /* lightSensorWarmUpTime= */ anyInt(), + /* brightnessMin= */ anyFloat(), + /* brightnessMax= */ anyFloat(), + /* dozeScaleFactor */ anyFloat(), + /* lightSensorRate= */ anyInt(), + /* initialLightSensorRate= */ anyInt(), + /* brighteningLightDebounceConfig */ anyLong(), + /* darkeningLightDebounceConfig */ anyLong(), + /* brighteningLightDebounceConfigIdle= */ anyLong(), + /* darkeningLightDebounceConfigIdle= */ anyLong(), + /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(), any(HysteresisLevels.class), any(HysteresisLevels.class), any(HysteresisLevels.class), @@ -1114,9 +1114,9 @@ public final class DisplayPowerControllerTest { eq(mContext), any(BrightnessRangeController.class), any(BrightnessThrottler.class), - isNull(), - anyInt(), - anyInt(), + /* idleModeBrightnessMapper= */ isNull(), + /* ambientLightHorizonShort= */ anyInt(), + /* ambientLightHorizonLong= */ anyInt(), eq(lux), eq(brightness) ); @@ -1386,8 +1386,6 @@ public final class DisplayPowerControllerTest { when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); return null; }).when(mHolder.displayPowerState).setScreenState(anyInt()); - // init displayoffload session and support offloading. - initDisplayOffloadSession(); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); // start with DOZE. @@ -1414,8 +1412,6 @@ public final class DisplayPowerControllerTest { when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); return null; }).when(mHolder.displayPowerState).setScreenState(anyInt()); - // init displayoffload session and support offloading. - initDisplayOffloadSession(); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); // start with DOZE. @@ -1441,8 +1437,6 @@ public final class DisplayPowerControllerTest { when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); return null; }).when(mHolder.displayPowerState).setScreenState(anyInt()); - // init displayoffload session and support offloading. - initDisplayOffloadSession(); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); // start with OFF. @@ -1458,28 +1452,6 @@ public final class DisplayPowerControllerTest { verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); } - private void initDisplayOffloadSession() { - mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() { - @Override - public boolean startOffload() { - return true; - } - - @Override - public void stopOffload() {} - }); - - mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() { - @Override - public void setDozeStateOverride(int displayState) {} - - @Override - public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() { - return mDisplayOffloader; - } - }; - } - private void advanceTime(long timeMs) { mClock.fastForward(timeMs); mTestLooper.dispatchAll(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java index a77a9586fb43..8270845657c6 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @@ -40,12 +41,12 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; -import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession; import android.hardware.display.DisplayManagerInternal.DisplayOffloader; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.PowerManager; import android.view.Display; import android.view.DisplayAddress; import android.view.SurfaceControl; @@ -118,10 +119,12 @@ public class LocalDisplayAdapterTest { private DisplayNotificationManager mMockedDisplayNotificationManager; @Mock private DisplayManagerFlags mFlags; + @Mock + private DisplayPowerControllerInterface mMockedDisplayPowerController; private Handler mHandler; - private DisplayOffloadSession mDisplayOffloadSession; + private DisplayOffloadSessionImpl mDisplayOffloadSession; private DisplayOffloader mDisplayOffloader; @@ -1195,6 +1198,7 @@ public class LocalDisplayAdapterTest { } verify(mDisplayOffloader, times(mDisplayOffloadSupportedStates.size())).startOffload(); + assertTrue(mDisplayOffloadSession.isActive()); } @Test @@ -1217,6 +1221,9 @@ public class LocalDisplayAdapterTest { changeStateToDozeRunnable.run(); verify(mDisplayOffloader).stopOffload(); + assertFalse(mDisplayOffloadSession.isActive()); + verify(mMockedDisplayPowerController).setBrightnessFromOffload( + PowerManager.BRIGHTNESS_INVALID_FLOAT); } private void initDisplayOffloadSession() { @@ -1230,15 +1237,8 @@ public class LocalDisplayAdapterTest { public void stopOffload() {} }); - mDisplayOffloadSession = new DisplayOffloadSession() { - @Override - public void setDozeStateOverride(int displayState) {} - - @Override - public DisplayOffloader getDisplayOffloader() { - return mDisplayOffloader; - } - }; + mDisplayOffloadSession = new DisplayOffloadSessionImpl(mDisplayOffloader, + mMockedDisplayPowerController); } private void setupCutoutAndRoundedCorners() { diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java index c4f483810478..52fa91f5fe0e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java @@ -42,7 +42,9 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.display.AutomaticBrightnessController; import com.android.server.display.BrightnessSetting; import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy; +import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy; import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy; +import com.android.server.display.feature.DisplayManagerFlags; import org.junit.Before; import org.junit.Test; @@ -66,6 +68,8 @@ public final class DisplayBrightnessControllerTest { private BrightnessSetting mBrightnessSetting; @Mock private Runnable mOnBrightnessChangeRunnable; + @Mock + private DisplayManagerFlags mDisplayManagerFlags; @Mock private HandlerExecutor mBrightnessChangeExecutor; @@ -74,7 +78,7 @@ public final class DisplayBrightnessControllerTest { DisplayBrightnessController.Injector() { @Override DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector( - Context context, int displayId) { + Context context, int displayId, DisplayManagerFlags flags) { return mDisplayBrightnessStrategySelector; } }; @@ -92,7 +96,7 @@ public final class DisplayBrightnessControllerTest { .thenReturn(true); mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector, DISPLAY_ID, DEFAULT_BRIGHTNESS, mBrightnessSetting, mOnBrightnessChangeRunnable, - mBrightnessChangeExecutor); + mBrightnessChangeExecutor, mDisplayManagerFlags); } @Test @@ -350,7 +354,7 @@ public final class DisplayBrightnessControllerTest { int nonDefaultDisplayId = 1; mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector, nonDefaultDisplayId, DEFAULT_BRIGHTNESS, mBrightnessSetting, - mOnBrightnessChangeRunnable, mBrightnessChangeExecutor); + mOnBrightnessChangeRunnable, mBrightnessChangeExecutor, mDisplayManagerFlags); brightness = 0.5f; when(mBrightnessSetting.getBrightness()).thenReturn(brightness); mDisplayBrightnessController.setAutomaticBrightnessController( @@ -384,4 +388,14 @@ public final class DisplayBrightnessControllerTest { verify(mBrightnessSetting).setBrightness(brightnessValue2); verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2); } + + @Test + public void setBrightnessFromOffload() { + float brightness = 0.4f; + OffloadBrightnessStrategy offloadBrightnessStrategy = mock(OffloadBrightnessStrategy.class); + when(mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()).thenReturn( + offloadBrightnessStrategy); + mDisplayBrightnessController.setBrightnessFromOffload(brightness); + verify(offloadBrightnessStrategy).setOffloadScreenBrightness(brightness); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index 8497dabba67d..37958faed1ca 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -17,6 +17,7 @@ package com.android.server.display.brightness; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -35,13 +36,16 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; +import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.brightness.strategy.BoostBrightnessStrategy; import com.android.server.display.brightness.strategy.DozeBrightnessStrategy; import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy; import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy; +import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy; import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy; import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy; import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy; +import com.android.server.display.feature.DisplayManagerFlags; import org.junit.Before; import org.junit.Rule; @@ -71,10 +75,64 @@ public final class DisplayBrightnessStrategySelectorTest { @Mock private FollowerBrightnessStrategy mFollowerBrightnessStrategy; @Mock + private AutomaticBrightnessStrategy mAutomaticBrightnessStrategy; + @Mock + private OffloadBrightnessStrategy mOffloadBrightnessStrategy; + @Mock private Resources mResources; + @Mock + private DisplayManagerFlags mDisplayManagerFlags; private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector; private Context mContext; + private DisplayBrightnessStrategySelector.Injector mInjector = + new DisplayBrightnessStrategySelector.Injector() { + @Override + ScreenOffBrightnessStrategy getScreenOffBrightnessStrategy() { + return mScreenOffBrightnessModeStrategy; + } + + @Override + DozeBrightnessStrategy getDozeBrightnessStrategy() { + return mDozeBrightnessModeStrategy; + } + + @Override + OverrideBrightnessStrategy getOverrideBrightnessStrategy() { + return mOverrideBrightnessStrategy; + } + + @Override + TemporaryBrightnessStrategy getTemporaryBrightnessStrategy() { + return mTemporaryBrightnessStrategy; + } + + @Override + BoostBrightnessStrategy getBoostBrightnessStrategy() { + return mBoostBrightnessStrategy; + } + + @Override + FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) { + return mFollowerBrightnessStrategy; + } + + @Override + InvalidBrightnessStrategy getInvalidBrightnessStrategy() { + return mInvalidBrightnessStrategy; + } + + @Override + AutomaticBrightnessStrategy getAutomaticBrightnessStrategy(Context context, + int displayId) { + return mAutomaticBrightnessStrategy; + } + + @Override + OffloadBrightnessStrategy getOffloadBrightnessStrategy() { + return mOffloadBrightnessStrategy; + } + }; @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @@ -87,45 +145,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mContext.getContentResolver()).thenReturn(contentResolver); when(mContext.getResources()).thenReturn(mResources); when(mInvalidBrightnessStrategy.getName()).thenReturn("InvalidBrightnessStrategy"); - DisplayBrightnessStrategySelector.Injector injector = - new DisplayBrightnessStrategySelector.Injector() { - @Override - ScreenOffBrightnessStrategy getScreenOffBrightnessStrategy() { - return mScreenOffBrightnessModeStrategy; - } - - @Override - DozeBrightnessStrategy getDozeBrightnessStrategy() { - return mDozeBrightnessModeStrategy; - } - - @Override - OverrideBrightnessStrategy getOverrideBrightnessStrategy() { - return mOverrideBrightnessStrategy; - } - - @Override - TemporaryBrightnessStrategy getTemporaryBrightnessStrategy() { - return mTemporaryBrightnessStrategy; - } - - @Override - BoostBrightnessStrategy getBoostBrightnessStrategy() { - return mBoostBrightnessStrategy; - } - - @Override - FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) { - return mFollowerBrightnessStrategy; - } - - @Override - InvalidBrightnessStrategy getInvalidBrightnessStrategy() { - return mInvalidBrightnessStrategy; - } - }; mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, - injector, DISPLAY_ID); + mInjector, DISPLAY_ID, mDisplayManagerFlags); } @@ -188,6 +209,7 @@ public final class DisplayBrightnessStrategySelectorTest { displayPowerRequest.screenBrightnessOverride = Float.NaN; when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); + when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest, Display.STATE_ON), mInvalidBrightnessStrategy); } @@ -200,4 +222,35 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest, Display.STATE_ON), mFollowerBrightnessStrategy); } + + @Test + public void selectStrategySelectsOffloadStrategyWhenValid() { + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( + DisplayManagerInternal.DisplayPowerRequest.class); + displayPowerRequest.screenBrightnessOverride = Float.NaN; + when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); + when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); + when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f); + assertEquals(mOffloadBrightnessStrategy, mDisplayBrightnessStrategySelector.selectStrategy( + displayPowerRequest, Display.STATE_ON)); + } + + @Test + public void selectStrategyDoesNotSelectOffloadStrategyWhenFeatureFlagDisabled() { + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(false); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( + DisplayManagerInternal.DisplayPowerRequest.class); + displayPowerRequest.screenBrightnessOverride = Float.NaN; + when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); + when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); + when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f); + assertNotEquals(mOffloadBrightnessStrategy, + mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest, + Display.STATE_ON)); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index b652576a75c8..78ec2ff31161 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -188,6 +188,29 @@ public class AutomaticBrightnessStrategyTest { } @Test + public void testAutoBrightnessState_BrightnessReasonIsOffload() { + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + int targetDisplayState = Display.STATE_ON; + boolean allowAutoBrightnessWhileDozing = false; + int brightnessReason = BrightnessReason.REASON_OFFLOAD; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController) + .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + mBrightnessConfiguration, + lastUserSetBrightness, + userSetBrightnessChanged, 0.5f, + false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); + } + + @Test public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() { mAutomaticBrightnessStrategy.setUseAutoBrightness(true); int targetDisplayState = Display.STATE_DOZE; diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java new file mode 100644 index 000000000000..36719af10abb --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 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.brightness.strategy; + +import static org.junit.Assert.assertEquals; + +import android.hardware.display.DisplayManagerInternal; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class OffloadBrightnessStrategyTest { + + private OffloadBrightnessStrategy mOffloadBrightnessStrategy; + + @Before + public void before() { + mOffloadBrightnessStrategy = new OffloadBrightnessStrategy(); + } + + @Test + public void testUpdateBrightnessWhenOffloadBrightnessIsSet() { + DisplayManagerInternal.DisplayPowerRequest + displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest(); + float brightness = 0.2f; + mOffloadBrightnessStrategy.setOffloadScreenBrightness(brightness); + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_OFFLOAD); + DisplayBrightnessState expectedDisplayBrightnessState = + new DisplayBrightnessState.Builder() + .setBrightness(brightness) + .setBrightnessReason(brightnessReason) + .setSdrBrightness(brightness) + .setDisplayBrightnessStrategyName(mOffloadBrightnessStrategy.getName()) + .setShouldUpdateScreenBrightnessSetting(true) + .build(); + DisplayBrightnessState updatedDisplayBrightnessState = + mOffloadBrightnessStrategy.updateBrightness(displayPowerRequest); + assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); + } +} diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java index 2d760a7dabac..1002fba3d60d 100644 --- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java @@ -220,7 +220,7 @@ public class PinnerServiceTest { private void setDeviceConfigPinnedAnonSize(long size) { mFakeDeviceConfigInterface.setProperty( - DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + DeviceConfig.NAMESPACE_RUNTIME_NATIVE, "pin_shared_anon_size", String.valueOf(size), /*makeDefault=*/false); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 0f3daec263e0..74eb79d7554c 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -28,6 +28,8 @@ import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUT import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -248,6 +250,45 @@ public class AuthSessionTest { } @Test + public void testOnErrorReceivedBeforeOnDialogAnimatedIn() throws RemoteException { + final int fingerprintId = 0; + final int faceId = 1; + setupFingerprint(fingerprintId, FingerprintSensorProperties.TYPE_REAR); + setupFace(faceId, true /* confirmationAlwaysRequired */, + mock(IBiometricAuthenticator.class)); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + + for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { + assertThat(sensor.getSensorState()).isEqualTo(BiometricSensor.STATE_WAITING_FOR_COOKIE); + session.onCookieReceived( + session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie()); + } + assertThat(session.allCookiesReceived()).isTrue(); + assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED); + + final BiometricSensor faceSensor = session.mPreAuthInfo.eligibleSensors.get(faceId); + final BiometricSensor fingerprintSensor = session.mPreAuthInfo.eligibleSensors.get( + fingerprintId); + final int cookie = faceSensor.getCookie(); + session.onErrorReceived(0, cookie, BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL, 0); + + assertThat(faceSensor.getSensorState()).isEqualTo(BiometricSensor.STATE_STOPPED); + assertThat(session.getState()).isEqualTo(STATE_ERROR_PENDING_SYSUI); + + session.onDialogAnimatedIn(true); + + assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED_UI_SHOWING); + assertThat(fingerprintSensor.getSensorState()).isEqualTo( + BiometricSensor.STATE_AUTHENTICATING); + } + + @Test public void testCancelReducesAppetiteForCookies() throws Exception { setupFace(0 /* id */, false /* confirmationAlwaysRequired */, mock(IBiometricAuthenticator.class)); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index d87b8d1cb60e..b5ba322b1a5e 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -773,6 +773,30 @@ public class VirtualDeviceManagerServiceTest { } @Test + public void getAllPersistentDeviceIds_respectsCurrentAssociations() { + mVdms.onCdmAssociationsChanged(List.of(mAssociationInfo)); + TestableLooper.get(this).processAllMessages(); + + assertThat(mLocalService.getAllPersistentDeviceIds()) + .containsExactly(mDeviceImpl.getPersistentDeviceId()); + + mVdms.onCdmAssociationsChanged(List.of( + createAssociationInfo(2, AssociationRequest.DEVICE_PROFILE_APP_STREAMING), + createAssociationInfo(3, AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION), + createAssociationInfo(4, AssociationRequest.DEVICE_PROFILE_WATCH))); + TestableLooper.get(this).processAllMessages(); + + assertThat(mLocalService.getAllPersistentDeviceIds()).containsExactly( + VirtualDeviceImpl.createPersistentDeviceId(2), + VirtualDeviceImpl.createPersistentDeviceId(3)); + + mVdms.onCdmAssociationsChanged(Collections.emptyList()); + TestableLooper.get(this).processAllMessages(); + + assertThat(mLocalService.getAllPersistentDeviceIds()).isEmpty(); + } + + @Test public void onAppsOnVirtualDeviceChanged_singleVirtualDevice_listenersNotified() { ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2)); mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); diff --git a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java index 1e73a45a0c22..5aef7a320930 100644 --- a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java @@ -90,7 +90,7 @@ public class AudioPoliciesDeviceRouteControllerTest { @Test public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() { - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); @@ -105,7 +105,7 @@ public class AudioPoliciesDeviceRouteControllerTest { audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @@ -117,7 +117,7 @@ public class AudioPoliciesDeviceRouteControllerTest { mController.selectRoute(MediaRoute2Info.TYPE_DOCK); - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); } @@ -135,7 +135,7 @@ public class AudioPoliciesDeviceRouteControllerTest { mController.selectRoute(MediaRoute2Info.TYPE_DOCK); - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); } @@ -155,7 +155,7 @@ public class AudioPoliciesDeviceRouteControllerTest { mController.selectRoute(null); - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @@ -171,7 +171,7 @@ public class AudioPoliciesDeviceRouteControllerTest { mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP); - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @@ -202,7 +202,7 @@ public class AudioPoliciesDeviceRouteControllerTest { mController.updateVolume(VOLUME_SAMPLE_1); - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1); } @@ -222,7 +222,7 @@ public class AudioPoliciesDeviceRouteControllerTest { mController.selectRoute(MediaRoute2Info.TYPE_DOCK); - MediaRoute2Info route2Info = mController.getDeviceRoute(); + MediaRoute2Info route2Info = mController.getSelectedRoute(); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1); } diff --git a/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java index 24e48517f280..aed68a5dc7b5 100644 --- a/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java @@ -104,7 +104,7 @@ public class LegacyDeviceRouteControllerTest { mOnDeviceRouteChangedListener ); - MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute(); assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) @@ -129,7 +129,7 @@ public class LegacyDeviceRouteControllerTest { mOnDeviceRouteChangedListener ); - MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute(); assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) @@ -243,7 +243,7 @@ public class LegacyDeviceRouteControllerTest { mOnDeviceRouteChangedListener ); - MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute(); assertThat(actualMediaRoute.getType()).isEqualTo(mExpectedRouteType); assertThat(TextUtils.equals(actualMediaRoute.getName(), mExpectedRouteNameValue)) @@ -310,7 +310,7 @@ public class LegacyDeviceRouteControllerTest { // Simulating wired device being connected. callAudioRoutesObserver(audioRoutesInfo); - MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute(); assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_HEADPHONES_NAME)) @@ -324,7 +324,7 @@ public class LegacyDeviceRouteControllerTest { AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute(); callAudioRoutesObserver(fakeBluetoothAudioRoute); - MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute(); assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) @@ -334,12 +334,12 @@ public class LegacyDeviceRouteControllerTest { @Test public void updateVolume_differentValue_updatesDeviceRouteVolume() { - MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute(); assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue(); - actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + actualMediaRoute = mDeviceRouteController.getSelectedRoute(); assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_VALUE_SAMPLE_1); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java index 4b6183dc9ffa..8bc027d50a3b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; @@ -39,6 +40,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -55,6 +57,9 @@ import java.util.concurrent.atomic.AtomicBoolean; @SmallTest @RunWith(AndroidJUnit4.class) public class ArchiveTest extends UiServiceTestCase { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int SIZE = 5; private NotificationManagerService.Archive mArchive; @@ -249,4 +254,29 @@ public class ArchiveTest extends UiServiceTestCase { assertThat(expected).contains(sbn.getKey()); } } + + @Test + public void testRemoveNotificationsByPackage() { + List<String> expected = new ArrayList<>(); + + StatusBarNotification sbn_remove = getNotification("pkg_remove", 0, + UserHandle.of(USER_CURRENT)); + mArchive.record(sbn_remove, REASON_CANCEL); + + StatusBarNotification sbn_keep = getNotification("pkg_keep", 1, + UserHandle.of(USER_CURRENT)); + mArchive.record(sbn_keep, REASON_CANCEL); + expected.add(sbn_keep.getKey()); + + StatusBarNotification sbn_remove2 = getNotification("pkg_remove", 2, + UserHandle.of(USER_CURRENT)); + mArchive.record(sbn_remove2, REASON_CANCEL); + + mArchive.removePackageNotifications("pkg_remove", USER_CURRENT); + List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true)); + assertThat(actual).hasSize(expected.size()); + for (StatusBarNotification sbn : actual) { + assertThat(expected).contains(sbn.getKey()); + } + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 7fb8b30dea06..b45dcd4d5403 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -65,6 +65,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Build.VERSION_CODES.P; +import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE; import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; @@ -72,7 +73,6 @@ import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; -import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; @@ -87,8 +87,6 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; -import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE; -import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; @@ -309,7 +307,6 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; - @SmallTest @RunWith(AndroidTestingRunner.class) @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. @@ -739,9 +736,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { clearInvocations(mRankingHandler); when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - mTestFlagResolver.setFlagOverride(FSI_FORCE_DEMOTE, false); - mTestFlagResolver.setFlagOverride(SHOW_STICKY_HUN_FOR_DENIED_FSI, false); - var checker = mock(TestableNotificationManagerService.ComponentPermissionChecker.class); mService.permissionChecker = checker; when(checker.check(anyString(), anyInt(), anyInt(), anyBoolean())) @@ -818,6 +812,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mPackageIntentReceiver.onReceive(getContext(), intent); } + private void simulatePackageRemovedBroadcast(String pkg, int uid) { + // mimics receive broadcast that package is removed, but doesn't remove the package. + final Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, + new String[]{pkg}); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid}); + + final Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED); + intent.setData(Uri.parse("package:" + pkg)); + intent.putExtras(extras); + + mPackageIntentReceiver.onReceive(getContext(), intent); + } + private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) { // mimics receive broadcast that package is (un)distracting // but does not actually register that info with packagemanager @@ -883,6 +891,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.setAllowBubbles(channelEnabled); } + private void setUpPrefsForHistory(int uid, boolean globalEnabled) { + // Sets NOTIFICATION_HISTORY_ENABLED setting for calling process uid + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0, uid); + // Sets NOTIFICATION_HISTORY_ENABLED setting for uid 0 + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0); + + // Forces an update by calling observe on mSettingsObserver, which picks up the settings + // changes above. + mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper); + + assertEquals(globalEnabled, Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0 /* =def */, uid) != 0); + } + private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) { Notification.Builder nb = new Notification.Builder(mContext, "a") .setContentTitle("foo") @@ -9836,6 +9860,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testHandleOnPackageRemoved_ClearsHistory() throws RemoteException { + // Enables Notification History setting + setUpPrefsForHistory(mUid, true /* =enabled */); + + // Posts a notification to the mTestNotificationChannel. + final NotificationRecord notif = generateNotificationRecord( + mTestNotificationChannel, 1, null, false); + mService.addNotification(notif); + StatusBarNotification[] notifs = mBinderService.getActiveNotifications( + notif.getSbn().getPackageName()); + assertEquals(1, notifs.length); + + // Cancels all notifications. + mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, + notif.getUserId(), REASON_CANCEL); + waitForIdle(); + notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName()); + assertEquals(0, notifs.length); + + // Checks that notification history's recently canceled archive contains the notification. + notifs = mBinderService.getHistoricalNotificationsWithAttribution(PKG, + mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */); + waitForIdle(); + assertEquals(1, notifs.length); + + // Remove sthe package that contained the channel + simulatePackageRemovedBroadcast(PKG, mUid); + waitForIdle(); + + // Checks that notification history no longer contains the notification. + notifs = mBinderService.getHistoricalNotificationsWithAttribution( + PKG, mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */); + waitForIdle(); + assertEquals(0, notifs.length); + } + + @Test public void testNotificationHistory_addNoisyNotification() throws Exception { NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, null /* tvExtender */); @@ -11472,14 +11533,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class)); } - private void verifyStickyHun(Flag flag, int permissionState, boolean appRequested, + private void verifyStickyHun(int permissionState, boolean appRequested, boolean isSticky) throws Exception { when(mPermissionHelper.hasRequestedPermission(Manifest.permission.USE_FULL_SCREEN_INTENT, PKG, mUserId)).thenReturn(appRequested); - mTestFlagResolver.setFlagOverride(flag, true); - when(mPermissionManager.checkPermissionForDataDelivery( eq(Manifest.permission.USE_FULL_SCREEN_INTENT), any(), any())) .thenReturn(permissionState); @@ -11503,8 +11562,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testFixNotification_flagEnableStickyHun_fsiPermissionHardDenied_showStickyHun() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true, /* isSticky= */ true); } @@ -11512,16 +11570,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testFixNotification_flagEnableStickyHun_fsiPermissionSoftDenied_showStickyHun() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true, /* isSticky= */ true); } @Test public void testFixNotification_fsiPermissionSoftDenied_appNotRequest_noShowStickyHun() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, false, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, false, /* isSticky= */ false); } @@ -11530,39 +11586,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testFixNotification_flagEnableStickyHun_fsiPermissionGranted_showFsi() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_GRANTED, true, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_GRANTED, true, /* isSticky= */ false); } @Test - public void testFixNotification_flagForceStickyHun_fsiPermissionHardDenied_showStickyHun() - throws Exception { - - verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true, - /* isSticky= */ true); - } - - @Test - public void testFixNotification_flagForceStickyHun_fsiPermissionSoftDenied_showStickyHun() - throws Exception { - - verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true, - /* isSticky= */ true); - } - - @Test - public void testFixNotification_flagForceStickyHun_fsiPermissionGranted_showStickyHun() - throws Exception { - - verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionManager.PERMISSION_GRANTED, true, - /* isSticky= */ true); - } - - @Test public void fixNotification_withFgsFlag_butIsNotFgs() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java index 5147a08b5216..29848d0139b1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java @@ -171,19 +171,8 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { } @Test - public void testGetFsiState_stickyHunFlagDisabled_zero() { - final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ false, - /* hasFullScreenIntent= */ true, - /* hasFsiRequestedButDeniedFlag= */ true, - /* eventType= */ NOTIFICATION_POSTED); - assertEquals(0, fsiState); - } - - @Test public void testGetFsiState_isUpdate_zero() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ true, /* hasFsiRequestedButDeniedFlag= */ true, /* eventType= */ NOTIFICATION_UPDATED); @@ -193,7 +182,6 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { @Test public void testGetFsiState_hasFsi_allowedEnum() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ true, /* hasFsiRequestedButDeniedFlag= */ false, /* eventType= */ NOTIFICATION_POSTED); @@ -203,7 +191,6 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { @Test public void testGetFsiState_fsiPermissionDenied_deniedEnum() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ false, /* hasFsiRequestedButDeniedFlag= */ true, /* eventType= */ NOTIFICATION_POSTED); @@ -213,7 +200,6 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { @Test public void testGetFsiState_noFsi_noFsiEnum() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ false, /* hasFsiRequestedButDeniedFlag= */ false, /* eventType= */ NOTIFICATION_POSTED); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index c156e376ce3e..fe21103096ca 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -5733,17 +5733,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testGetFsiState_flagDisabled_zero() { - final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission */ true, /* isFlagEnabled= */ false); - - assertEquals(0, fsiState); - } - - @Test public void testGetFsiState_appDidNotRequest_enumNotRequested() { final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission */ false, /* isFlagEnabled= */ true); + /* requestedFsiPermission */ false); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, fsiState); } @@ -5754,7 +5746,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { .thenReturn(PermissionManager.PERMISSION_GRANTED); final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission= */ true, /* isFlagEnabled= */ true); + /* requestedFsiPermission= */ true); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, fsiState); } @@ -5765,7 +5757,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { .thenReturn(PermissionManager.PERMISSION_SOFT_DENIED); final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true); + /* requestedFsiPermission = */ true); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState); } @@ -5776,7 +5768,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { .thenReturn(PermissionManager.PERMISSION_HARD_DENIED); final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true); + /* requestedFsiPermission = */ true); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState); } @@ -5785,18 +5777,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testIsFsiPermissionUserSet_appDidNotRequest_false() { final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, - /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ true); - - assertFalse(isUserSet); - } - - @Test - public void testIsFsiPermissionUserSet_flagDisabled_false() { - final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, - /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, - /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ false); + /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET); assertFalse(isUserSet); } @@ -5805,8 +5786,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testIsFsiPermissionUserSet_userSet_true() { final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, - /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ true); + /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET); assertTrue(isUserSet); } @@ -5815,8 +5795,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testIsFsiPermissionUserSet_notUserSet_false() { final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, - /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ true); + /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET); assertFalse(isUserSet); } diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index 1b8d746f271f..e83f03d155aa 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -99,4 +99,7 @@ android_test { enabled: false, }, + data: [ + ":OverlayTestApp", + ], } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 762e23c8e288..f2a1fe859634 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -119,6 +119,9 @@ </intent-filter> </activity> + <activity android:name="com.android.server.wm.ActivityRecordInputSinkTests$TestActivity" + android:exported="true"> + </activity> </application> <instrumentation diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml index 2717ef901216..f8ebeaddcb7e 100644 --- a/services/tests/wmtests/AndroidTest.xml +++ b/services/tests/wmtests/AndroidTest.xml @@ -21,6 +21,7 @@ <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> <option name="test-file-name" value="WmTests.apk" /> + <option name="test-file-name" value="OverlayTestApp.apk" /> </target_preparer> <option name="test-tag" value="WmTests" /> diff --git a/services/tests/wmtests/OverlayApp/Android.bp b/services/tests/wmtests/OverlayApp/Android.bp new file mode 100644 index 000000000000..77d5b2295dc9 --- /dev/null +++ b/services/tests/wmtests/OverlayApp/Android.bp @@ -0,0 +1,19 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "OverlayTestApp", + + srcs: ["**/*.java"], + + resource_dirs: ["res"], + + certificate: "platform", + platform_apis: true, +} diff --git a/services/tests/wmtests/OverlayApp/AndroidManifest.xml b/services/tests/wmtests/OverlayApp/AndroidManifest.xml new file mode 100644 index 000000000000..5b4ef577112a --- /dev/null +++ b/services/tests/wmtests/OverlayApp/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.overlay_app"> + <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" /> + + <application> + <activity android:name=".OverlayApp" + android:exported="true" + android:theme="@style/TranslucentFloatingTheme"> + </activity> + </application> +</manifest> diff --git a/services/tests/wmtests/OverlayApp/res/values/styles.xml b/services/tests/wmtests/OverlayApp/res/values/styles.xml new file mode 100644 index 000000000000..fff10a3f29ac --- /dev/null +++ b/services/tests/wmtests/OverlayApp/res/values/styles.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <style name="TranslucentFloatingTheme" > + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowIsFloating">true</item> + <item name="android:windowNoTitle">true</item> + + <!-- Disables starting window. --> + <item name="android:windowDisablePreview">true</item> + </style> +</resources> diff --git a/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java b/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java new file mode 100644 index 000000000000..89161c51dc53 --- /dev/null +++ b/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 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.wm.overlay_app; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.view.Gravity; +import android.view.WindowManager; +import android.widget.LinearLayout; + +/** + * Test app that is translucent not touchable modal. + * If launched with "disableInputSink" extra boolean value, this activity disables + * ActivityRecordInputSinkEnabled as long as the permission is granted. + */ +public class OverlayApp extends Activity { + private static final String KEY_DISABLE_INPUT_SINK = "disableInputSink"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout tv = new LinearLayout(this); + tv.setBackgroundColor(Color.GREEN); + tv.setPadding(50, 50, 50, 50); + tv.setGravity(Gravity.CENTER); + setContentView(tv); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + + if (getIntent().getBooleanExtra(KEY_DISABLE_INPUT_SINK, false)) { + setActivityRecordInputSinkEnabled(false); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java new file mode 100644 index 000000000000..3b280d9d45bd --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java @@ -0,0 +1,209 @@ +/* + * Copyright 2023 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.wm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.UiAutomation; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.SystemClock; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.view.MotionEvent; +import android.view.ViewGroup; +import android.widget.Button; +import android.window.WindowInfosListenerForTest; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.window.flags.Flags; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Internal variant of {@link android.server.wm.window.ActivityRecordInputSinkTests}. + */ +@MediumTest +@RunWith(AndroidJUnit4.class) +public class ActivityRecordInputSinkTests { + private static final String OVERLAY_APP_PKG = "com.android.server.wm.overlay_app"; + private static final String OVERLAY_ACTIVITY = OVERLAY_APP_PKG + "/.OverlayApp"; + private static final String KEY_DISABLE_INPUT_SINK = "disableInputSink"; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Rule + public final ActivityScenarioRule<TestActivity> mActivityRule = + new ActivityScenarioRule<>(TestActivity.class); + + private UiAutomation mUiAutomation; + + @Before + public void setUp() { + mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + } + + @After + public void tearDown() { + ActivityManager am = + InstrumentationRegistry.getInstrumentation().getContext().getSystemService( + ActivityManager.class); + mUiAutomation.adoptShellPermissionIdentity(); + try { + am.forceStopPackage(OVERLAY_APP_PKG); + } finally { + mUiAutomation.dropShellPermissionIdentity(); + } + } + + @Test + public void testSimpleButtonPress() { + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(1, a.mNumClicked); + }); + } + + @Test + public void testSimpleButtonPress_withOverlay() throws InterruptedException { + startOverlayApp(false); + waitForOverlayApp(); + + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(0, a.mNumClicked); + }); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_DISABLE_ACTIVITY_RECORD_INPUT_SINK) + public void testSimpleButtonPress_withOverlayDisableInputSink() throws InterruptedException { + startOverlayApp(true); + waitForOverlayApp(); + + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(1, a.mNumClicked); + }); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ALLOW_DISABLE_ACTIVITY_RECORD_INPUT_SINK) + public void testSimpleButtonPress_withOverlayDisableInputSink_flagDisabled() + throws InterruptedException { + startOverlayApp(true); + waitForOverlayApp(); + + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(0, a.mNumClicked); + }); + } + + private void startOverlayApp(boolean disableInputSink) { + String launchCommand = "am start -n " + OVERLAY_ACTIVITY; + if (disableInputSink) { + launchCommand += " --ez " + KEY_DISABLE_INPUT_SINK + " true"; + } + + mUiAutomation.adoptShellPermissionIdentity(); + try { + mUiAutomation.executeShellCommand(launchCommand); + } finally { + mUiAutomation.dropShellPermissionIdentity(); + } + } + + private void waitForOverlayApp() throws InterruptedException { + final var listenerHost = new WindowInfosListenerForTest(); + final var latch = new CountDownLatch(1); + final Consumer<List<WindowInfosListenerForTest.WindowInfo>> listener = windowInfos -> { + final boolean inputSinkReady = windowInfos.stream().anyMatch(info -> + info.isVisible + && info.name.contains("ActivityRecordInputSink " + OVERLAY_ACTIVITY)); + if (inputSinkReady) { + latch.countDown(); + } + }; + + listenerHost.addWindowInfosListener(listener); + try { + assertTrue(latch.await(5, TimeUnit.SECONDS)); + } finally { + listenerHost.removeWindowInfosListener(listener); + } + } + + private void injectTapOnButton() { + Rect buttonBounds = new Rect(); + mActivityRule.getScenario().onActivity(a -> { + a.mButton.getBoundsOnScreen(buttonBounds); + }); + final int x = buttonBounds.centerX(); + final int y = buttonBounds.centerY(); + + MotionEvent down = MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0); + mUiAutomation.injectInputEvent(down, true); + + SystemClock.sleep(10); + + MotionEvent up = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), + MotionEvent.ACTION_UP, x, y, 0); + mUiAutomation.injectInputEvent(up, true); + + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + public static class TestActivity extends Activity { + int mNumClicked = 0; + Button mButton; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mButton = new Button(this); + mButton.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + setContentView(mButton); + mButton.setOnClickListener(v -> mNumClicked++); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index e152feb141e1..e31ee1174656 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -306,7 +306,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { @Test public void testCachedStateConfigurationChange() throws RemoteException { - doNothing().when(mClientLifecycleManager).scheduleTransactionItem(any(), any()); + doNothing().when(mClientLifecycleManager).scheduleTransactionItemUnlocked(any(), any()); final IApplicationThread thread = mWpc.getThread(); final Configuration newConfig = new Configuration(mWpc.getConfiguration()); newConfig.densityDpi += 100; @@ -322,18 +322,17 @@ public class WindowProcessControllerTests extends WindowTestsBase { newConfig.densityDpi += 100; mWpc.onConfigurationChanged(newConfig); verify(mClientLifecycleManager, never()).scheduleTransactionItem(eq(thread), any()); + verify(mClientLifecycleManager, never()).scheduleTransactionItemUnlocked(eq(thread), any()); // Cached -> non-cached will send the previous deferred config immediately. mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER); final ArgumentCaptor<ConfigurationChangeItem> captor = ArgumentCaptor.forClass(ConfigurationChangeItem.class); - verify(mClientLifecycleManager).scheduleTransactionItem(eq(thread), captor.capture()); + verify(mClientLifecycleManager).scheduleTransactionItemUnlocked( + eq(thread), captor.capture()); final ClientTransactionHandler client = mock(ClientTransactionHandler.class); captor.getValue().preExecute(client); - final ArgumentCaptor<Configuration> configCaptor = - ArgumentCaptor.forClass(Configuration.class); - verify(client).updatePendingConfiguration(configCaptor.capture()); - assertEquals(newConfig, configCaptor.getValue()); + verify(client).updatePendingConfiguration(newConfig); } @Test diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 63de41f80c23..4c56f33af430 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1953,6 +1953,13 @@ public class UsageStatsService extends SystemService implements } } + // Flags status. + pw.println("Flags:"); + pw.println(" " + Flags.FLAG_USER_INTERACTION_TYPE_API + + ": " + Flags.userInteractionTypeApi()); + pw.println(" " + Flags.FLAG_USE_PARCELED_LIST + + ": " + Flags.useParceledList()); + final int[] userIds; synchronized (mLock) { final int userCount = mUserState.size(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index a584fc9b2216..b77596391be1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -2437,8 +2437,6 @@ public class VoiceInteractionManagerService extends SystemService { synchronized (VoiceInteractionManagerServiceStub.this) { Slog.i(TAG, "Force stopping current voice recognizer: " + getCurRecognizer(userHandle)); - // TODO: Figure out why the interactor was being cleared and document it. - setCurInteractor(null, userHandle); initRecognizer(userHandle); } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index f8608b8fead2..e12a815a84f5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -23,6 +23,7 @@ import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.DurationMillisLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -70,6 +71,7 @@ import android.util.Pair; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.util.HandlerExecutor; import com.android.internal.util.FunctionalUtils; import com.android.internal.util.Preconditions; @@ -95,7 +97,22 @@ import java.util.function.Consumer; import java.util.stream.Collectors; /** - * Subscription manager provides the mobile subscription information. + * Subscription manager provides the mobile subscription information that are associated with the + * calling user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U) + * and below can see all subscriptions as it does today. + * + * <p>For example, if we have + * <ul> + * <li> Subscription 1 associated with personal profile. + * <li> Subscription 2 associated with work profile. + * </ul> + * Then for SDK 35+, if the caller identity is personal profile, then + * {@link #getActiveSubscriptionInfoList} will return subscription 1 only and vice versa. + * + * <p>If the caller needs to see all subscriptions across user profiles, + * use {@link #createForAllUserProfiles} to convert the instance to see all. Additional permission + * may be required as documented on the each API. + * */ @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -1446,6 +1463,16 @@ public class SubscriptionManager { } } + /** + * {@code true} if the SubscriptionManager instance should see all subscriptions regardless its + * association with particular user profile. + * + * <p> It only applies to Android SDK 35(V) and above. For Android SDK 34(U) and below, the + * caller can see all subscription across user profiles as it does today today even if it's + * {@code false}. + */ + private boolean mIsForAllUserProfiles = false; + /** @hide */ @UnsupportedAppUsage public SubscriptionManager(Context context) { @@ -1776,8 +1803,23 @@ public class SubscriptionManager { } /** - * Get the SubscriptionInfo(s) of the currently active SIM(s). The records will be sorted - * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}. + * Get the SubscriptionInfo(s) of the currently active SIM(s) associated with the current caller + * user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U) + * and below can see all subscriptions as it does today. + * + * <p>For example, if we have + * <ul> + * <li> Subscription 1 associated with personal profile. + * <li> Subscription 2 associated with work profile. + * </ul> + * Then for SDK 35+, if the caller identity is personal profile, then this will return + * subscription 1 only and vice versa. + * + * <p>If the caller needs to see all subscriptions across user profiles, + * use {@link #createForAllUserProfiles} to convert this instance to see all. + * + * <p> The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by + * {@link SubscriptionInfo#getSubscriptionId}. * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see @@ -1800,8 +1842,25 @@ public class SubscriptionManager { * </ul> */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + // @RequiresPermission(TODO(b/308809058)) public List<SubscriptionInfo> getActiveSubscriptionInfoList() { - return getActiveSubscriptionInfoList(/* userVisibleonly */true); + List<SubscriptionInfo> activeList = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), + mContext.getAttributionTag(), mIsForAllUserProfiles); + } + } catch (RemoteException ex) { + // ignore it + } + + if (activeList != null) { + activeList = activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo)) + .collect(Collectors.toList()); + } + return activeList; } /** @@ -1835,6 +1894,26 @@ public class SubscriptionManager { } /** + * Convert this subscription manager instance into one that can see all subscriptions across + * user profiles. + * + * @return a SubscriptionManager that can see all subscriptions regardless its user profile + * association. + * + * @see #getActiveSubscriptionInfoList + * @see #getActiveSubscriptionInfoCount + * @see UserHandle + */ + @FlaggedApi(Flags.FLAG_WORK_PROFILE_API_SPLIT) + // @RequiresPermission(TODO(b/308809058)) + // The permission check for accessing all subscriptions will be enforced upon calling the + // individual APIs linked above. + @NonNull public SubscriptionManager createForAllUserProfiles() { + mIsForAllUserProfiles = true; + return this; + } + + /** * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly * is true, it will filter out the hidden subscriptions. * @@ -1847,7 +1926,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag(), true /*isForAllUserProfiles*/); } } catch (RemoteException ex) { // ignore it @@ -2002,13 +2081,19 @@ public class SubscriptionManager { } /** - * Get the active subscription count. + * Get the active subscription count associated with the current caller user profile for + * Android SDK 35(V) and above, while Android SDK 34(U) and below can see all subscriptions as + * it does today. + * + * <p>If the caller needs to see all subscriptions across user profiles, + * use {@link #createForAllUserProfiles} to convert this instance to see all. * * @return The current number of active subscriptions. * * @see #getActiveSubscriptionInfoList() */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + // @RequiresPermission(TODO(b/308809058)) public int getActiveSubscriptionInfoCount() { int result = 0; @@ -2016,7 +2101,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag(), mIsForAllUserProfiles); } } catch (RemoteException ex) { // ignore it diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index d2dbeb7aff74..3f41d5667107 100644 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -66,6 +66,8 @@ interface ISub { * * @param callingPackage The package maing the call. * @param callingFeatureId The feature in the package + * @param isForAllProfiles whether the caller intends to see all subscriptions regardless + * association. * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device. * <ul> * <li> @@ -83,14 +85,17 @@ interface ISub { * </ul> */ List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage, - String callingFeatureId); + String callingFeatureId, boolean isForAllProfiles); /** * @param callingPackage The package making the call. * @param callingFeatureId The feature in the package. + * @param isForAllProfile whether the caller intends to see all subscriptions regardless + * association. * @return the number of active subscriptions */ - int getActiveSubInfoCount(String callingPackage, String callingFeatureId); + int getActiveSubInfoCount(String callingPackage, String callingFeatureId, + boolean isForAllProfile); /** * @return the maximum number of subscriptions this device will support at any one time. diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt index b44f1a607b05..c49f8fecfdee 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt @@ -23,10 +23,13 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_FINISH_ACTIVITY import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -53,7 +56,12 @@ class CloseImeToHomeOnFinishActivityTest(flicker: LegacyFlickerTest) : BaseTest( testApp.launchViaIntent(wmHelper) testApp.openIME(wmHelper) } - transitions { testApp.finishActivity(wmHelper) } + transitions { + broadcastActionTrigger.doAction(ACTION_FINISH_ACTIVITY) + wmHelper.StateSyncBuilder() + .withActivityRemoved(ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent()) + .waitForAndVerify() + } teardown { simpleApp.exit(wmHelper) } } diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt index 976ac82c8bb5..994edc592f5d 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt @@ -28,6 +28,7 @@ import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper +import com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_TOGGLE_ORIENTATION import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -53,7 +54,11 @@ class OpenImeWindowToFixedPortraitAppTest(flicker: LegacyFlickerTest) : BaseTest // Enable letterbox when the app calls setRequestedOrientation device.executeShellCommand("cmd window set-ignore-orientation-request true") } - transitions { testApp.toggleFixPortraitOrientation(wmHelper) } + transitions { + broadcastActionTrigger.doAction(ACTION_TOGGLE_ORIENTATION) + // Ensure app relaunching transition finished and the IME was shown + testApp.waitIMEShown(wmHelper) + } teardown { testApp.exit() device.executeShellCommand("cmd window set-ignore-orientation-request false") diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt index aff8e657bec0..6ee5a9a775b3 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt @@ -104,7 +104,7 @@ class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: LegacyFlicke @Presubmit @Test - open fun imeLayerIsVisibleWhenSwitchingToImeApp() { + fun imeLayerIsVisibleWhenSwitchingToImeApp() { flicker.assertLayersStart { isVisible(ComponentNameMatcher.IME) } flicker.assertLayersTag(TAG_IME_VISIBLE) { isVisible(ComponentNameMatcher.IME) } flicker.assertLayersEnd { isVisible(ComponentNameMatcher.IME) } diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt index 4ffdcea455ba..1ad5c0de282b 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt @@ -93,7 +93,7 @@ class ShowImeOnAppStartWhenLaunchingAppTest(flicker: LegacyFlickerTest) : BaseTe } transitions { testApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() + testApp.waitIMEShown(wmHelper) } } diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt index 6ad235ce892e..181a2a229940 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt @@ -23,11 +23,14 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import android.tools.device.traces.parsers.toFlickerComponent import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_START_DIALOG_THEMED_ACTIVITY import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.FixMethodOrder @@ -50,8 +53,12 @@ class ShowImeWhileDismissingThemedPopupDialogTest(flicker: LegacyFlickerTest) : override val transition: FlickerBuilder.() -> Unit = { setup { testApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() - testApp.startDialogThemedActivity(wmHelper) + testApp.waitIMEShown(wmHelper) + broadcastActionTrigger.doAction(ACTION_START_DIALOG_THEMED_ACTIVITY) + wmHelper.StateSyncBuilder() + .withFullScreenApp( + ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent()) + .waitForAndVerify() // Verify IME insets isn't visible on dialog since it's non-IME focusable window assertFalse(testApp.getInsetsVisibleFromDialog(ime())) assertTrue(testApp.getInsetsVisibleFromDialog(statusBars())) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt index 7c9c05d7da85..ad272a052220 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -17,6 +17,7 @@ package com.android.server.wm.flicker import android.app.Instrumentation +import android.content.Intent import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerBuilderProvider @@ -50,6 +51,19 @@ constructor( /** Specification of the test transition to execute */ abstract val transition: FlickerBuilder.() -> Unit + protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) + + // Helper class to process test actions by broadcast. + protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) { + private fun createIntentWithAction(broadcastAction: String): Intent { + return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + } + + fun doAction(broadcastAction: String) { + instrumentation.context.sendBroadcast(createIntentWithAction(broadcastAction)) + } + } + /** * Entry point for the test runner. It will use this method to initialize and cache flicker * executions diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt index 252f7d3e1bed..cb1aab0bfeea 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt @@ -50,7 +50,7 @@ constructor( waitIMEShown(wmHelper) } - protected fun waitIMEShown(wmHelper: WindowManagerStateHelper) { + fun waitIMEShown(wmHelper: WindowManagerStateHelper) { wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() } @@ -63,17 +63,4 @@ constructor( uiDevice.pressBack() wmHelper.StateSyncBuilder().withImeGone().waitForAndVerify() } - - open fun finishActivity(wmHelper: WindowManagerStateHelper) { - val finishButton = - uiDevice.wait( - Until.findObject(By.res(packageName, "finish_activity_btn")), - FIND_TIMEOUT - ) - requireNotNull(finishButton) { - "Finish activity button not found, probably IME activity is not on the screen?" - } - finishButton.click() - wmHelper.StateSyncBuilder().withActivityRemoved(this).waitForAndVerify() - } } diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt index d3cee645cd99..0ee7aeeb30c6 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt @@ -74,24 +74,6 @@ constructor( open(expectedPackage) } - fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) { - val button = - uiDevice.wait( - Until.findObject(By.res(packageName, "start_dialog_themed_activity_btn")), - FIND_TIMEOUT - ) - - requireNotNull(button) { - "Button not found, this usually happens when the device " + - "was left in an unknown state (e.g. Screen turned off)" - } - button.click() - wmHelper - .StateSyncBuilder() - .withFullScreenApp(ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent()) - .waitForAndVerify() - } - fun dismissDialog(wmHelper: WindowManagerStateHelper) { val dialog = uiDevice.wait(Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT) @@ -126,20 +108,4 @@ constructor( } return false } - - fun toggleFixPortraitOrientation(wmHelper: WindowManagerStateHelper) { - val button = - uiDevice.wait( - Until.findObject(By.res(packageName, "toggle_fixed_portrait_btn")), - FIND_TIMEOUT - ) - require(button != null) { - "Button not found, this usually happens when the device " + - "was left in an unknown state (e.g. Screen turned off)" - } - button.click() - instrumentation.waitForIdleSync() - // Ensure app relaunching transition finish and the IME has shown - waitIMEShown(wmHelper) - } } diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml index fa73e2c63780..507c1b622613 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- Copyright 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,39 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" + android:background="@android:color/holo_green_light" android:focusableInTouchMode="true" - android:background="@android:color/holo_green_light"> - <EditText android:id="@+id/plain_text_input" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:imeOptions="flagNoExtractUi" - android:inputType="text"/> - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical"> + + <EditText + android:id="@+id/plain_text_input" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - <Button - android:id="@+id/finish_activity_btn" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Finish activity" /> - <Button - android:id="@+id/start_dialog_themed_activity_btn" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Dialog themed activity" /> - <ToggleButton - android:id="@+id/toggle_fixed_portrait_btn" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textOn="Portrait (On)" - android:textOff="Portrait (Off)" - /> - </LinearLayout> + android:layout_height="wrap_content" + android:imeOptions="flagNoExtractUi" + android:inputType="text" /> </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 8b334c0a8588..80c1dd072df7 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -40,6 +40,18 @@ public class ActivityOptions { public static final String LABEL = "ImeActivity"; public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".ImeActivity"); + + /** Intent action used to finish the test activity. */ + public static final String ACTION_FINISH_ACTIVITY = + FLICKER_APP_PACKAGE + ".ImeActivity.FINISH_ACTIVITY"; + + /** Intent action used to start a {@link DialogThemedActivity}. */ + public static final String ACTION_START_DIALOG_THEMED_ACTIVITY = + FLICKER_APP_PACKAGE + ".ImeActivity.START_DIALOG_THEMED_ACTIVITY"; + + /** Intent action used to toggle activity orientation. */ + public static final String ACTION_TOGGLE_ORIENTATION = + FLICKER_APP_PACKAGE + ".ImeActivity.TOGGLE_ORIENTATION"; } public static class AutoFocusActivity { diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java index d7ee2af44111..4418b5a5ff82 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java @@ -16,12 +16,51 @@ package com.android.server.wm.flicker.testapp; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + +import static com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_FINISH_ACTIVITY; +import static com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_START_DIALOG_THEMED_ACTIVITY; +import static com.android.server.wm.flicker.testapp.ActivityOptions.Ime.Default.ACTION_TOGGLE_ORIENTATION; + import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; +import android.util.Log; import android.view.WindowManager; -import android.widget.Button; public class ImeActivity extends Activity { + + private static final String TAG = "ImeActivity"; + + /** Receiver used to handle actions coming from the test helper methods. */ + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case ACTION_FINISH_ACTIVITY -> finish(); + case ACTION_START_DIALOG_THEMED_ACTIVITY -> startActivity( + new Intent(context, DialogThemedActivity.class)); + case ACTION_TOGGLE_ORIENTATION -> { + mIsPortrait = !mIsPortrait; + setRequestedOrientation(mIsPortrait + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_UNSPECIFIED); + } + default -> Log.w(TAG, "Unhandled action=" + intent.getAction()); + } + } + }; + + /** + * Used to toggle activity orientation between portrait when {@code true} and + * unspecified otherwise. + */ + private boolean mIsPortrait = false; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -30,9 +69,17 @@ public class ImeActivity extends Activity { .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; getWindow().setAttributes(p); setContentView(R.layout.activity_ime); - Button button = findViewById(R.id.finish_activity_btn); - button.setOnClickListener(view -> { - finish(); - }); + + final var filter = new IntentFilter(); + filter.addAction(ACTION_FINISH_ACTIVITY); + filter.addAction(ACTION_START_DIALOG_THEMED_ACTIVITY); + filter.addAction(ACTION_TOGGLE_ORIENTATION); + registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED); + } + + @Override + protected void onDestroy() { + unregisterReceiver(mBroadcastReceiver); + super.onDestroy(); } } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java index 7ee8debddcf1..cd711f72d8fd 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java @@ -16,29 +16,12 @@ package com.android.server.wm.flicker.testapp; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - -import android.content.Intent; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ToggleButton; - public class ImeActivityAutoFocus extends ImeActivity { @Override protected void onStart() { super.onStart(); - Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn); - startThemedActivityButton.setOnClickListener( - button -> startActivity(new Intent(this, DialogThemedActivity.class))); - - ToggleButton toggleFixedPortraitButton = findViewById(R.id.toggle_fixed_portrait_btn); - toggleFixedPortraitButton.setOnCheckedChangeListener( - (button, isChecked) -> setRequestedOrientation( - isChecked ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_UNSPECIFIED)); - - EditText editTextField = findViewById(R.id.plain_text_input); + final var editTextField = findViewById(R.id.plain_text_input); editTextField.requestFocus(); } } diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp index 15aaa463cce7..83ced2c2258f 100644 --- a/tests/InputScreenshotTest/Android.bp +++ b/tests/InputScreenshotTest/Android.bp @@ -7,12 +7,27 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +filegroup { + name: "InputScreenshotTestRNGFiles", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + exclude_srcs: [ + "src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt", + "src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt", + ], +} + android_test { name: "InputScreenshotTests", srcs: [ "src/**/*.java", "src/**/*.kt", ], + exclude_srcs: [ + "src/android/input/screenshot/package-info.java", + ], platform_apis: true, certificate: "platform", static_libs: [ @@ -43,6 +58,7 @@ android_test { "hamcrest-library", "kotlin-test", "flag-junit", + "platform-parametric-runner-lib", "platform-test-annotations", "services.core.unboosted", "testables", diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp new file mode 100644 index 000000000000..912f4b8069b4 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/Android.bp @@ -0,0 +1,71 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_library { + name: "InputRoboRNGTestsAssetsLib", + asset_dirs: ["assets"], + sdk_version: "current", + platform_apis: true, + manifest: "AndroidManifest.xml", + optimize: { + enabled: false, + }, + use_resource_processor: true, +} + +android_app { + name: "InputRoboApp", + srcs: [], + static_libs: [ + "androidx.test.espresso.core", + "androidx.appcompat_appcompat", + "flag-junit", + "guava", + "InputRoboRNGTestsAssetsLib", + "platform-screenshot-diff-core", + "PlatformComposeSceneTransitionLayoutTestsUtils", + ], + manifest: "robo-manifest.xml", + aaptflags: [ + "--extra-packages", + "com.android.input.screenshot", + ], + dont_merge_manifests: true, + platform_apis: true, + system_ext_specific: true, + certificate: "platform", + privileged: true, + resource_dirs: [], + kotlincflags: ["-Xjvm-default=all"], + + plugins: ["dagger2-compiler"], + use_resource_processor: true, +} + +android_robolectric_test { + name: "InputRoboRNGTests", + srcs: [ + ":InputScreenshotTestRNGFiles", + ":flag-junit", + ":platform-test-screenshot-rules", + ], + // Do not add any new libraries here, they should be added to SystemUIGoogleRobo above. + static_libs: [ + "androidx.compose.runtime_runtime", + "androidx.test.uiautomator_uiautomator", + "androidx.test.ext.junit", + "inline-mockito-robolectric-prebuilt", + "platform-parametric-runner-lib", + "uiautomator-helpers", + ], + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + "truth", + ], + upstream: true, + java_resource_dirs: ["config"], + instrumentation_for: "InputRoboApp", +} diff --git a/tests/InputScreenshotTest/robotests/AndroidManifest.xml b/tests/InputScreenshotTest/robotests/AndroidManifest.xml new file mode 100644 index 000000000000..56893113288d --- /dev/null +++ b/tests/InputScreenshotTest/robotests/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2023 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.input.screenshot"> + <uses-sdk android:minSdkVersion="21"/> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> +</manifest> diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png Binary files differnew file mode 100644 index 000000000000..baf204a6cfb3 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png Binary files differnew file mode 100644 index 000000000000..deb3ceeca7fb --- /dev/null +++ b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png diff --git a/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png Binary files differnew file mode 100644 index 000000000000..34e25f73d953 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png diff --git a/tests/InputScreenshotTest/robotests/config/robolectric.properties b/tests/InputScreenshotTest/robotests/config/robolectric.properties new file mode 100644 index 000000000000..83d7549551ce --- /dev/null +++ b/tests/InputScreenshotTest/robotests/config/robolectric.properties @@ -0,0 +1,15 @@ +# 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. +# +sdk=NEWEST_SDK
\ No newline at end of file diff --git a/tests/InputScreenshotTest/robotests/robo-manifest.xml b/tests/InputScreenshotTest/robotests/robo-manifest.xml new file mode 100644 index 000000000000..e86f58ef0e55 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/robo-manifest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!--- Include all the namespaces we will ever need anywhere, because this is the source the manifest merger uses for namespaces --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.input.screenshot" + coreApp="true"> + <application> + <activity + android:name="androidx.activity.ComponentActivity" + android:exported="true"> + </activity> + </application> +</manifest> diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt index c2c3d5530a00..75dab41d3609 100644 --- a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt +++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt @@ -18,6 +18,7 @@ package com.android.input.screenshot import android.content.Context import android.graphics.Bitmap +import android.os.Build import androidx.activity.ComponentActivity import androidx.compose.foundation.Image import androidx.compose.ui.platform.ViewRootForTest @@ -49,15 +50,17 @@ class InputScreenshotTestRule( ) ) private val composeRule = createAndroidComposeRule<ComponentActivity>() - private val delegateRule = - RuleChain.outerRule(colorsRule) - .around(deviceEmulationRule) + private val roboRule = + RuleChain.outerRule(deviceEmulationRule) .around(screenshotRule) .around(composeRule) + private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule) private val matcher = UnitTestBitmapMatcher + private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false override fun apply(base: Statement, description: Description): Statement { - return delegateRule.apply(base, description) + val ruleToApply = if (isRobolectric) roboRule else delegateRule + return ruleToApply.apply(base, description) } /** @@ -84,4 +87,4 @@ class InputScreenshotTestRule( val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher) } -}
\ No newline at end of file +} diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt index 8ae6dfd8b63b..ab7bb4eda899 100644 --- a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt +++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt @@ -26,14 +26,15 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters import platform.test.screenshot.DeviceEmulationSpec /** A screenshot test for Keyboard layout preview for Iso physical layout. */ -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) { companion object { - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") @JvmStatic fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal } @@ -55,4 +56,4 @@ class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) } } -}
\ No newline at end of file +} diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java new file mode 100644 index 000000000000..4b5a56d3bd1d --- /dev/null +++ b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java @@ -0,0 +1,4 @@ +@GraphicsMode(GraphicsMode.Mode.NATIVE) +package com.android.input.screenshot; + +import org.robolectric.annotation.GraphicsMode; diff --git a/tests/SmokeTestApps/Android.bp b/tests/SmokeTestApps/Android.bp index 3505fe1c4afb..38ee8ac99747 100644 --- a/tests/SmokeTestApps/Android.bp +++ b/tests/SmokeTestApps/Android.bp @@ -11,4 +11,7 @@ android_test { name: "SmokeTestTriggerApps", srcs: ["src/**/*.java"], sdk_version: "current", + errorprone: { + enabled: false, + }, } diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt index ff0fe32ad267..493ad56a5cbb 100644 --- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt +++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt @@ -78,6 +78,9 @@ class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host class com.android.internal.util.FastPrintWriter keepclass class com.android.internal.util.LineBreakBufferedWriter keepclass +class android.util.EventLog stubclass +class android.util.EventLog !com.android.hoststubgen.nativesubstitution.EventLog_host +class android.util.EventLog$Event stubclass # Expose Context because it's referred to by AndroidTestCase, but don't need to expose any of # its members. diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java new file mode 100644 index 000000000000..292e8da0de10 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 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.hoststubgen.nativesubstitution; + +import android.util.Log; +import android.util.Log.Level; + +import java.util.Collection; + +public class EventLog_host { + public static int writeEvent(int tag, int value) { + return writeEvent(tag, (Object) value); + } + + public static int writeEvent(int tag, long value) { + return writeEvent(tag, (Object) value); + } + + public static int writeEvent(int tag, float value) { + return writeEvent(tag, (Object) value); + } + + public static int writeEvent(int tag, String str) { + return writeEvent(tag, (Object) str); + } + + public static int writeEvent(int tag, Object... list) { + final StringBuilder sb = new StringBuilder(); + sb.append("logd: [event] "); + final String tagName = android.util.EventLog.getTagName(tag); + if (tagName != null) { + sb.append(tagName); + } else { + sb.append(tag); + } + sb.append(": ["); + for (int i = 0; i < list.length; i++) { + sb.append(String.valueOf(list[i])); + if (i < list.length - 1) { + sb.append(','); + } + } + sb.append(']'); + System.out.println(sb.toString()); + return sb.length(); + } + + public static void readEvents(int[] tags, Collection<android.util.EventLog.Event> output) { + throw new UnsupportedOperationException(); + } + + public static void readEventsOnWrapping(int[] tags, long timestamp, + Collection<android.util.EventLog.Event> output) { + throw new UnsupportedOperationException(); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java index 4a3a79803b65..668c94c0f91c 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -27,9 +27,10 @@ import java.util.concurrent.atomic.AtomicLong; /** * Tentative, partial implementation of the Parcel native methods, using Java's - * {@link ByteBuffer}. It turned out there's enough semantics differences between Parcel - * and {@link ByteBuffer}, so it didn't actually work. - * (e.g. Parcel seems to allow moving the data position to be beyond its size? Which + * {@code byte[]}. + * (We don't use a {@link ByteBuffer} because there's enough semantics differences between Parcel + * and {@link ByteBuffer}, and it didn't work out. + * e.g. Parcel seems to allow moving the data position to be beyond its size? Which * {@link ByteBuffer} wouldn't allow...) */ public class Parcel_host { diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh index 89daa2084420..85038be80c51 100755 --- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh +++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# This command is expected to be executed with: atest hoststubgen-invoke-test + set -e # Exit when any command files # This script runs HostStubGen directly with various arguments and make sure @@ -35,6 +37,12 @@ if [[ "$TEMP" == "" ]] ; then mkdir -p $TEMP fi +cleanup_temp() { + rm -fr $TEMP/* +} + +cleanup_temp + JAR=hoststubgen-test-tiny-framework.jar STUB=$TEMP/stub.jar IMPL=$TEMP/impl.jar @@ -47,12 +55,10 @@ HOSTSTUBGEN_OUT=$TEMP/output.txt # HostStubGen result in it. HOSTSTUBGEN_RC=0 -# Define the functions to - - # Note, because the build rule will only install hoststubgen.jar, but not the wrapper script, # we need to execute it manually with the java command. hoststubgen() { + echo "Running hoststubgen with: $*" java -jar ./hoststubgen.jar "$@" } @@ -62,7 +68,7 @@ run_hoststubgen() { echo "# Test: $test_name" - rm -f $HOSTSTUBGEN_OUT + cleanup_temp local filter_arg="" @@ -73,11 +79,21 @@ run_hoststubgen() { cat $ANNOTATION_FILTER fi + local stub_arg="" + local impl_arg="" + + if [[ "$STUB" != "" ]] ; then + stub_arg="--out-stub-jar $STUB" + fi + if [[ "$IMPL" != "" ]] ; then + impl_arg="--out-impl-jar $IMPL" + fi + hoststubgen \ --debug \ --in-jar $JAR \ - --out-stub-jar $STUB \ - --out-impl-jar $IMPL \ + $stub_arg \ + $impl_arg \ --stub-annotation \ android.hosttest.annotation.HostSideTestStub \ --keep-annotation \ @@ -105,6 +121,21 @@ run_hoststubgen() { return 0 } +assert_file_generated() { + local file="$1" + if [[ "$file" == "" ]] ; then + if [[ -f "$file" ]] ; then + echo "HostStubGen shouldn't have generated $file" + return 1 + fi + else + if ! [[ -f "$file" ]] ; then + echo "HostStubGen didn't generate $file" + return 1 + fi + fi +} + run_hoststubgen_for_success() { run_hoststubgen "$@" @@ -112,6 +143,9 @@ run_hoststubgen_for_success() { echo "HostStubGen expected to finish successfully, but failed with $rc" return 1 fi + + assert_file_generated "$STUB" + assert_file_generated "$IMPL" } run_hoststubgen_for_failure() { @@ -189,6 +223,11 @@ run_hoststubgen_for_success "One specific class disallowed, but it doesn't use a * # All other classes allowed " +STUB="" run_hoststubgen_for_success "No stub generation" "" + +IMPL="" run_hoststubgen_for_success "No impl generation" "" + +STUB="" IMPL="" run_hoststubgen_for_success "No stub, no impl generation" "" echo "All tests passed" diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt index 07bd2dc7c867..3cdddc23b332 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt @@ -237,8 +237,8 @@ class HostStubGen(val options: HostStubGenOptions) { */ private fun convert( inJar: String, - outStubJar: String, - outImplJar: String, + outStubJar: String?, + outImplJar: String?, filter: OutputFilter, enableChecker: Boolean, classes: ClassNodes, @@ -254,8 +254,8 @@ class HostStubGen(val options: HostStubGenOptions) { log.withIndent { // Open the input jar file and process each entry. ZipFile(inJar).use { inZip -> - ZipOutputStream(FileOutputStream(outStubJar)).use { stubOutStream -> - ZipOutputStream(FileOutputStream(outImplJar)).use { implOutStream -> + maybeWithZipOutputStream(outStubJar) { stubOutStream -> + maybeWithZipOutputStream(outImplJar) { implOutStream -> val inEntries = inZip.entries() while (inEntries.hasMoreElements()) { val entry = inEntries.nextElement() @@ -265,22 +265,29 @@ class HostStubGen(val options: HostStubGenOptions) { log.i("Converted all entries.") } } - log.i("Created stub: $outStubJar") - log.i("Created impl: $outImplJar") + outStubJar?.let { log.i("Created stub: $it") } + outImplJar?.let { log.i("Created impl: $it") } } } val end = System.currentTimeMillis() log.v("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0) } + private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T { + if (filename == null) { + return block(null) + } + return ZipOutputStream(FileOutputStream(filename)).use(block) + } + /** * Convert a single ZIP entry, which may or may not be a class file. */ private fun convertSingleEntry( inZip: ZipFile, entry: ZipEntry, - stubOutStream: ZipOutputStream, - implOutStream: ZipOutputStream, + stubOutStream: ZipOutputStream?, + implOutStream: ZipOutputStream?, filter: OutputFilter, packageRedirector: PackageRedirectRemapper, enableChecker: Boolean, @@ -316,8 +323,8 @@ class HostStubGen(val options: HostStubGenOptions) { // Unknown type, we just copy it to both output zip files. // TODO: We probably shouldn't do it for stub jar? log.v("Copying: %s", entry.name) - copyZipEntry(inZip, entry, stubOutStream) - copyZipEntry(inZip, entry, implOutStream) + stubOutStream?.let { copyZipEntry(inZip, entry, it) } + implOutStream?.let { copyZipEntry(inZip, entry, it) } } } @@ -346,8 +353,8 @@ class HostStubGen(val options: HostStubGenOptions) { private fun processSingleClass( inZip: ZipFile, entry: ZipEntry, - stubOutStream: ZipOutputStream, - implOutStream: ZipOutputStream, + stubOutStream: ZipOutputStream?, + implOutStream: ZipOutputStream?, filter: OutputFilter, packageRedirector: PackageRedirectRemapper, enableChecker: Boolean, @@ -361,7 +368,7 @@ class HostStubGen(val options: HostStubGenOptions) { return } // Generate stub first. - if (classPolicy.policy.needsInStub) { + if (stubOutStream != null && classPolicy.policy.needsInStub) { log.v("Creating stub class: %s Policy: %s", classInternalName, classPolicy) log.withIndent { BufferedInputStream(inZip.getInputStream(entry)).use { bis -> @@ -374,8 +381,8 @@ class HostStubGen(val options: HostStubGenOptions) { } } } - log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy) - if (classPolicy.policy.needsInImpl) { + if (implOutStream != null && classPolicy.policy.needsInImpl) { + log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy) log.withIndent { BufferedInputStream(inZip.getInputStream(entry)).use { bis -> val newEntry = ZipEntry(entry.name) diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt index da5348707528..83f873d38f1b 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt @@ -28,10 +28,10 @@ class HostStubGenOptions( var inJar: String = "", /** Output stub jar file */ - var outStubJar: String = "", + var outStubJar: String? = null, /** Output implementation jar file */ - var outImplJar: String = "", + var outImplJar: String? = null, var inputJarDumpFile: String? = null, @@ -71,7 +71,7 @@ class HostStubGenOptions( var enablePreTrace: Boolean = false, var enablePostTrace: Boolean = false, - var enableNonStubMethodCallDetection: Boolean = true, + var enableNonStubMethodCallDetection: Boolean = false, ) { companion object { @@ -209,11 +209,14 @@ class HostStubGenOptions( if (ret.inJar.isEmpty()) { throw ArgumentsException("Required option missing: --in-jar") } - if (ret.outStubJar.isEmpty()) { - throw ArgumentsException("Required option missing: --out-stub-jar") + if (ret.outStubJar == null && ret.outImplJar == null) { + log.w("Neither --out-stub-jar nor --out-impl-jar is set." + + " $COMMAND_NAME will not generate jar files.") } - if (ret.outImplJar.isEmpty()) { - throw ArgumentsException("Required option missing: --out-impl-jar") + + if (ret.enableNonStubMethodCallDetection) { + log.w("--enable-non-stub-method-check is not fully implemented yet." + + " See the todo in doesMethodNeedNonStubCallCheck().") } return ret diff --git a/tools/hoststubgen/hoststubgen/test-framework/README.md b/tools/hoststubgen/hoststubgen/test-framework/README.md index 20e2f873b152..f616ad61d219 100644 --- a/tools/hoststubgen/hoststubgen/test-framework/README.md +++ b/tools/hoststubgen/hoststubgen/test-framework/README.md @@ -14,12 +14,6 @@ tests. $ atest --no-bazel-mode HostStubGenTest-framework-test-host-test ``` -- With `run-ravenwood-test` - -``` -$ run-ravenwood-test HostStubGenTest-framework-test-host-test -``` - - Advanced option: `run-test-without-atest.sh` runs the test without using `atest` or `run-ravenwood-test` ``` diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md index f3c0450d42a3..3bfad9bd673b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md @@ -13,12 +13,6 @@ This test doesn't use the actual android framework code. $ atest hoststubgen-test-tiny-test ``` -- With `run-ravenwood-test` should work too. This is the proper way to run it. - -``` -$ run-ravenwood-test hoststubgen-test-tiny-test -``` - - `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without using the build system. This is useful for debugging the tool. diff --git a/tools/hoststubgen/scripts/Android.bp b/tools/hoststubgen/scripts/Android.bp index 5da805e5640e..b1ba07ec540d 100644 --- a/tools/hoststubgen/scripts/Android.bp +++ b/tools/hoststubgen/scripts/Android.bp @@ -18,9 +18,3 @@ genrule_defaults { tools: ["dump-jar"], cmd: "$(location dump-jar) -s -o $(out) $(in)", } - -sh_binary_host { - name: "run-ravenwood-test", - src: "run-ravenwood-test", - visibility: ["//visibility:public"], -} diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh index 2dac08969d44..82faa91e2cac 100755 --- a/tools/hoststubgen/scripts/run-all-tests.sh +++ b/tools/hoststubgen/scripts/run-all-tests.sh @@ -22,10 +22,10 @@ cd .. READY_TEST_MODULES=( HostStubGenTest-framework-all-test-host-test hoststubgen-test-tiny-test + CtsUtilTestCasesRavenwood ) MUST_BUILD_MODULES=( - run-ravenwood-test "${NOT_READY_TEST_MODULES[*]}" HostStubGenTest-framework-test ) @@ -51,8 +51,6 @@ run ./scripts/build-framework-hostside-jars-and-extract.sh # run ./scripts/build-framework-hostside-jars-without-genrules.sh # These tests should all pass. -run-ravenwood-test ${READY_TEST_MODULES[*]} - -run atest CtsUtilTestCasesRavenwood +run atest ${READY_TEST_MODULES[*]} echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!"
\ No newline at end of file diff --git a/tools/hoststubgen/scripts/run-ravenwood-test b/tools/hoststubgen/scripts/run-ravenwood-test deleted file mode 100755 index 9bbb859f5c3d..000000000000 --- a/tools/hoststubgen/scripts/run-ravenwood-test +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/bash -# Copyright (C) 2023 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. - -set -e - -# Script to run a "Ravenwood" host side test. -# -# A proper way to run these tests is to use `atest`, but `atest` has a known issue of loading -# unrelated jar files as the class path, so for now we use this script to run host side tests. - -# Copy (with some changes) some functions from ../common.sh, so this script can be used without it. - -m() { - if (( $SKIP_BUILD )) ; then - echo "Skipping build: $*" 1>&2 - return 0 - fi - run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" -} - -run() { - echo "Running: $*" 1>&2 - "$@" -} - -run_junit_test_jar() { - local jar="$1" - echo "Starting test: $jar ..." - run cd "${jar%/*}" - - run ${JAVA:-java} $JAVA_OPTS \ - -cp $jar \ - org.junit.runner.JUnitCore \ - com.android.hoststubgen.hosthelper.HostTestSuite || return 1 - return 0 -} - -help() { - cat <<'EOF' - - run-ravenwood-test -- Run Ravenwood host tests - - Usage: - run-ravenwood-test [options] MODULE-NAME ... - - Options: - -h: Help - -t: Run test only, without building - -b: Build only, without running - - Example: - run-ravenwood-test HostStubGenTest-framework-test-host-test - -EOF -} - -#------------------------------------------------------------------------- -# Parse options -#------------------------------------------------------------------------- -build=0 -test=0 - -while getopts "htb" opt; do - case "$opt" in - h) help; exit 0 ;; - t) - test=1 - ;; - b) - build=1 - ;; - esac -done -shift $(($OPTIND - 1)) - -# If neither -t nor -b is provided, then build and run./ -if (( ( $build + $test ) == 0 )) ; then - build=1 - test=1 -fi - - -modules=("${@}") - -if (( "${#modules[@]}" == 0 )); then - help - exit 1 -fi - -#------------------------------------------------------------------------- -# Build -#------------------------------------------------------------------------- -if (( $build )) ; then - run m "${modules[@]}" -fi - -#------------------------------------------------------------------------- -# Run -#------------------------------------------------------------------------- - -failures=0 -if (( test )) ; then - for module in "${modules[@]}"; do - if run_junit_test_jar "$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/${module}/${module}.jar"; then - : # passed. - else - failures=$(( failures + 1 )) - fi - done - - if (( $failures > 0 )) ; then - echo "$failures test jar(s) failed." 1>&2 - exit 2 - fi -fi - -exit 0
\ No newline at end of file |