Merge "[speech] add binder alive check" into main
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 9d8f1f4..01486c0 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -16,19 +16,10 @@
package com.android.commands.uinput;
-import android.util.JsonReader;
-import android.util.JsonToken;
-import android.util.Log;
import android.util.SparseArray;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.IntStream;
import src.com.android.commands.uinput.InputAbsInfo;
@@ -40,46 +31,44 @@
private static final String TAG = "UinputEvent";
enum Command {
- REGISTER("register"),
- DELAY("delay"),
- INJECT("inject"),
- SYNC("sync");
-
- final String mCommandName;
-
- Command(String command) {
- mCommandName = command;
- }
+ REGISTER,
+ DELAY,
+ INJECT,
+ SYNC,
}
- private static final int EV_KEY = 0x01;
- private static final int EV_REL = 0x02;
- private static final int EV_ABS = 0x03;
- private static final int EV_MSC = 0x04;
- private static final int EV_SW = 0x05;
- private static final int EV_LED = 0x11;
- private static final int EV_SND = 0x12;
- private static final int EV_FF = 0x15;
+ // Constants representing evdev event types, from include/uapi/linux/input-event-codes.h in the
+ // kernel.
+ public static final int EV_KEY = 0x01;
+ public static final int EV_REL = 0x02;
+ public static final int EV_ABS = 0x03;
+ public static final int EV_MSC = 0x04;
+ public static final int EV_SW = 0x05;
+ public static final int EV_LED = 0x11;
+ public static final int EV_SND = 0x12;
+ public static final int EV_FF = 0x15;
- private enum UinputControlCode {
- UI_SET_EVBIT("UI_SET_EVBIT", 100),
- UI_SET_KEYBIT("UI_SET_KEYBIT", 101),
- UI_SET_RELBIT("UI_SET_RELBIT", 102),
- UI_SET_ABSBIT("UI_SET_ABSBIT", 103),
- UI_SET_MSCBIT("UI_SET_MSCBIT", 104),
- UI_SET_LEDBIT("UI_SET_LEDBIT", 105),
- UI_SET_SNDBIT("UI_SET_SNDBIT", 106),
- UI_SET_FFBIT("UI_SET_FFBIT", 107),
- UI_SET_SWBIT("UI_SET_SWBIT", 109),
- UI_SET_PROPBIT("UI_SET_PROPBIT", 110);
+ public enum UinputControlCode {
+ UI_SET_EVBIT(100),
+ UI_SET_KEYBIT(101),
+ UI_SET_RELBIT(102),
+ UI_SET_ABSBIT(103),
+ UI_SET_MSCBIT(104),
+ UI_SET_LEDBIT(105),
+ UI_SET_SNDBIT(106),
+ UI_SET_FFBIT(107),
+ UI_SET_SWBIT(109),
+ UI_SET_PROPBIT(110);
- final String mName;
- final int mValue;
+ private final int mValue;
- UinputControlCode(String name, int value) {
- this.mName = name;
+ UinputControlCode(int value) {
this.mValue = value;
}
+
+ public int getValue() {
+ return mValue;
+ }
}
// These constants come from "include/uapi/linux/input.h" in the kernel
@@ -104,9 +93,9 @@
private Bus mBus;
private int[] mInjections;
private SparseArray<int[]> mConfiguration;
- private int mDuration;
+ private int mDurationMillis;
private int mFfEffectsMax = 0;
- private String mInputport;
+ private String mInputPort;
private SparseArray<InputAbsInfo> mAbsInfo;
private String mSyncToken;
@@ -138,12 +127,20 @@
return mInjections;
}
+ /**
+ * Returns a {@link SparseArray} describing the event codes that should be registered for the
+ * device. The keys are uinput ioctl codes (such as those returned from {@link
+ * UinputControlCode#getValue()}, while the values are arrays of event codes to be enabled with
+ * those ioctls. For example, key 101 (corresponding to {@link UinputControlCode#UI_SET_KEYBIT})
+ * could have values 0x110 ({@code BTN_LEFT}, 0x111 ({@code BTN_RIGHT}), and 0x112
+ * ({@code BTN_MIDDLE}).
+ */
public SparseArray<int[]> getConfiguration() {
return mConfiguration;
}
- public int getDuration() {
- return mDuration;
+ public int getDurationMillis() {
+ return mDurationMillis;
}
public int getFfEffectsMax() {
@@ -155,7 +152,7 @@
}
public String getPort() {
- return mInputport;
+ return mInputPort;
}
public String getSyncToken() {
@@ -174,13 +171,13 @@
+ ", bus=" + mBus
+ ", events=" + Arrays.toString(mInjections)
+ ", configuration=" + mConfiguration
- + ", duration=" + mDuration
+ + ", duration=" + mDurationMillis + "ms"
+ ", ff_effects_max=" + mFfEffectsMax
- + ", port=" + mInputport
+ + ", port=" + mInputPort
+ "}";
}
- private static class Builder {
+ public static class Builder {
private Event mEvent;
Builder() {
@@ -191,15 +188,8 @@
mEvent.mId = id;
}
- private void setCommand(String command) {
- Objects.requireNonNull(command, "Command must not be null");
- for (Command cmd : Command.values()) {
- if (cmd.mCommandName.equals(command)) {
- mEvent.mCommand = cmd;
- return;
- }
- }
- throw new IllegalStateException("Unrecognized command: " + command);
+ public void setCommand(String command) {
+ mEvent.mCommand = Command.valueOf(command.toUpperCase());
}
public void setName(String name) {
@@ -210,6 +200,12 @@
mEvent.mInjections = events;
}
+ /**
+ * Sets the event codes that should be registered with a {@code register} command.
+ *
+ * @param configuration An array of ioctls and event codes, as described at
+ * {@link Event#getConfiguration()}.
+ */
public void setConfiguration(SparseArray<int[]> configuration) {
mEvent.mConfiguration = configuration;
}
@@ -226,8 +222,8 @@
mEvent.mBus = bus;
}
- public void setDuration(int duration) {
- mEvent.mDuration = duration;
+ public void setDurationMillis(int durationMillis) {
+ mEvent.mDurationMillis = durationMillis;
}
public void setFfEffectsMax(int ffEffectsMax) {
@@ -238,8 +234,8 @@
mEvent.mAbsInfo = absInfo;
}
- public void setInputport(String port) {
- mEvent.mInputport = port;
+ public void setInputPort(String port) {
+ mEvent.mInputPort = port;
}
public void setSyncToken(String syncToken) {
@@ -260,7 +256,7 @@
}
}
case DELAY -> {
- if (mEvent.mDuration <= 0) {
+ if (mEvent.mDurationMillis <= 0) {
throw new IllegalStateException("Delay has missing or invalid duration");
}
}
@@ -278,343 +274,4 @@
return mEvent;
}
}
-
- /**
- * A class that parses the JSON event format from an input stream to build device events.
- */
- public static class Reader {
- private JsonReader mReader;
-
- public Reader(InputStreamReader in) {
- mReader = new JsonReader(in);
- mReader.setLenient(true);
- }
-
- /**
- * Get next event entry from JSON file reader.
- */
- public Event getNextEvent() throws IOException {
- Event e = null;
- while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
- Event.Builder eb = new Event.Builder();
- try {
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "id":
- eb.setId(readInt());
- break;
- case "command":
- eb.setCommand(mReader.nextString());
- break;
- case "name":
- eb.setName(mReader.nextString());
- break;
- case "vid":
- eb.setVid(readInt());
- break;
- case "pid":
- eb.setPid(readInt());
- break;
- case "bus":
- eb.setBus(readBus());
- break;
- case "events":
- int[] injections = readInjectedEvents().stream()
- .mapToInt(Integer::intValue).toArray();
- eb.setInjections(injections);
- break;
- case "configuration":
- eb.setConfiguration(readConfiguration());
- break;
- case "ff_effects_max":
- eb.setFfEffectsMax(readInt());
- break;
- case "abs_info":
- eb.setAbsInfo(readAbsInfoArray());
- break;
- case "duration":
- eb.setDuration(readInt());
- break;
- case "port":
- eb.setInputport(mReader.nextString());
- break;
- case "syncToken":
- eb.setSyncToken(mReader.nextString());
- break;
- default:
- mReader.skipValue();
- }
- }
- mReader.endObject();
- } catch (IllegalStateException ex) {
- error("Error reading in object, ignoring.", ex);
- consumeRemainingElements();
- mReader.endObject();
- continue;
- }
- e = eb.build();
- }
-
- return e;
- }
-
- private ArrayList<Integer> readInjectedEvents() throws IOException {
- ArrayList<Integer> data = new ArrayList<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- // Read events in groups of three, because we expect an event type, event code,
- // and event value.
- final int type = readEvdevEventType();
- data.add(type);
- data.add(readEvdevEventCode(type));
- data.add(readInt());
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return data;
- }
-
- private int readValueAsInt(Function<String, Integer> stringToInt) throws IOException {
- switch (mReader.peek()) {
- case NUMBER: {
- return mReader.nextInt();
- }
- case STRING: {
- final var str = mReader.nextString();
- try {
- // Attempt to first parse the value as an int.
- return Integer.decode(str);
- } catch (NumberFormatException e) {
- // Then fall back to the supplied function.
- return stringToInt.apply(str);
- }
- }
- default: {
- throw new IllegalStateException(
- "Encountered malformed data. Expected int or string.");
- }
- }
- }
-
- private int readInt() throws IOException {
- return readValueAsInt((str) -> {
- throw new IllegalStateException("Encountered malformed data. Expected int.");
- });
- }
-
- private Bus readBus() throws IOException {
- String val = mReader.nextString();
- return Bus.valueOf(val.toUpperCase());
- }
-
- private SparseArray<int[]> readConfiguration()
- throws IllegalStateException, IOException {
- SparseArray<int[]> configuration = new SparseArray<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- UinputControlCode controlCode = null;
- IntStream data = null;
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "type":
- controlCode = readUinputControlCode();
- break;
- case "data":
- Objects.requireNonNull(controlCode,
- "Configuration 'type' must be specified before 'data'.");
- data = readDataForControlCode(controlCode)
- .stream().mapToInt(Integer::intValue);
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException(
- "Invalid key in device configuration: " + name);
- }
- }
- mReader.endObject();
- if (controlCode != null && data != null) {
- final int[] existing = configuration.get(controlCode.mValue);
- configuration.put(controlCode.mValue, existing == null ? data.toArray()
- : IntStream.concat(IntStream.of(existing), data).toArray());
- }
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return configuration;
- }
-
- private UinputControlCode readUinputControlCode() throws IOException {
- var code = readValueAsInt((controlTypeStr) -> {
- for (UinputControlCode controlCode : UinputControlCode.values()) {
- if (controlCode.mName.equals(controlTypeStr)) {
- return controlCode.mValue;
- }
- }
- return -1;
- });
- for (UinputControlCode controlCode : UinputControlCode.values()) {
- if (controlCode.mValue == code) {
- return controlCode;
- }
- }
- return null;
- }
-
- private List<Integer> readDataForControlCode(
- UinputControlCode controlCode) throws IOException {
- return switch (controlCode) {
- case UI_SET_EVBIT -> readArrayAsInts(this::readEvdevEventType);
- case UI_SET_KEYBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_KEY));
- case UI_SET_RELBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_REL));
- case UI_SET_ABSBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_ABS));
- case UI_SET_MSCBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_MSC));
- case UI_SET_LEDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_LED));
- case UI_SET_SNDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SND));
- case UI_SET_FFBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_FF));
- case UI_SET_SWBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SW));
- case UI_SET_PROPBIT -> readArrayAsInts(this::readEvdevInputProp);
- };
- }
-
- interface IntValueReader {
- int readNextValue() throws IOException;
- }
-
- private ArrayList<Integer> readArrayAsInts(
- IntValueReader nextValueReader) throws IOException {
- ArrayList<Integer> data = new ArrayList<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- data.add(nextValueReader.readNextValue());
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return data;
- }
-
- private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
- InputAbsInfo absInfo = new InputAbsInfo();
- try {
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "value":
- absInfo.value = readInt();
- break;
- case "minimum":
- absInfo.minimum = readInt();
- break;
- case "maximum":
- absInfo.maximum = readInt();
- break;
- case "fuzz":
- absInfo.fuzz = readInt();
- break;
- case "flat":
- absInfo.flat = readInt();
- break;
- case "resolution":
- absInfo.resolution = readInt();
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Invalid key in abs info: " + name);
- }
- }
- mReader.endObject();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return absInfo;
- }
-
- private SparseArray<InputAbsInfo> readAbsInfoArray()
- throws IllegalStateException, IOException {
- SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- int type = 0;
- InputAbsInfo absInfo = null;
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "code":
- type = readEvdevEventCode(EV_ABS);
- break;
- case "info":
- absInfo = readAbsInfo();
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Invalid key in abs info array: "
- + name);
- }
- }
- mReader.endObject();
- if (absInfo != null) {
- infoArray.put(type, absInfo);
- }
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return infoArray;
- }
-
- private int readEvdevEventType() throws IOException {
- return readValueAsInt(Device::getEvdevEventTypeByLabel);
- }
-
- private int readEvdevEventCode(int type) throws IOException {
- return readValueAsInt((str) -> Device.getEvdevEventCodeByLabel(type, str));
- }
-
- private int readEvdevInputProp() throws IOException {
- return readValueAsInt(Device::getEvdevInputPropByLabel);
- }
-
- private void consumeRemainingElements() throws IOException {
- while (mReader.hasNext()) {
- mReader.skipValue();
- }
- }
- }
-
- private static void error(String msg, Exception e) {
- System.out.println(msg);
- Log.e(TAG, msg);
- if (e != null) {
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
new file mode 100644
index 0000000..53d0be8
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -0,0 +1,333 @@
+/*
+ * 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.util.JsonReader;
+import android.util.JsonToken;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+
+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 {
+ private static final String TAG = "UinputJsonStyleParser";
+
+ private JsonReader mReader;
+
+ public JsonStyleParser(InputStreamReader in) {
+ mReader = new JsonReader(in);
+ mReader.setLenient(true);
+ }
+
+ /**
+ * Gets the next event entry from the JSON file.
+ */
+ public Event getNextEvent() throws IOException {
+ Event e = null;
+ while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
+ Event.Builder eb = new Event.Builder();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "id" -> eb.setId(readInt());
+ case "command" -> eb.setCommand(mReader.nextString());
+ case "name" -> eb.setName(mReader.nextString());
+ case "vid" -> eb.setVid(readInt());
+ case "pid" -> eb.setPid(readInt());
+ case "bus" -> eb.setBus(readBus());
+ case "events" -> {
+ int[] injections = readInjectedEvents().stream()
+ .mapToInt(Integer::intValue).toArray();
+ eb.setInjections(injections);
+ }
+ case "configuration" -> eb.setConfiguration(readConfiguration());
+ case "ff_effects_max" -> eb.setFfEffectsMax(readInt());
+ case "abs_info" -> eb.setAbsInfo(readAbsInfoArray());
+ case "duration" -> eb.setDurationMillis(readInt());
+ case "port" -> eb.setInputPort(mReader.nextString());
+ case "syncToken" -> eb.setSyncToken(mReader.nextString());
+ default -> mReader.skipValue();
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException ex) {
+ error("Error reading in object, ignoring.", ex);
+ consumeRemainingElements();
+ mReader.endObject();
+ continue;
+ }
+ e = eb.build();
+ }
+
+ return e;
+ }
+
+ private ArrayList<Integer> readInjectedEvents() throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ // Read events in groups of three, because we expect an event type, event code,
+ // and event value.
+ final int type = readEvdevEventType();
+ data.add(type);
+ data.add(readEvdevEventCode(type));
+ data.add(readInt());
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
+ private int readValueAsInt(Function<String, Integer> stringToInt) throws IOException {
+ switch (mReader.peek()) {
+ case NUMBER: {
+ return mReader.nextInt();
+ }
+ case STRING: {
+ final var str = mReader.nextString();
+ try {
+ // Attempt to first parse the value as an int.
+ return Integer.decode(str);
+ } catch (NumberFormatException e) {
+ // Then fall back to the supplied function.
+ return stringToInt.apply(str);
+ }
+ }
+ default: {
+ throw new IllegalStateException(
+ "Encountered malformed data. Expected int or string.");
+ }
+ }
+ }
+
+ private int readInt() throws IOException {
+ return readValueAsInt((str) -> {
+ throw new IllegalStateException("Encountered malformed data. Expected int.");
+ });
+ }
+
+ private Event.Bus readBus() throws IOException {
+ String val = mReader.nextString();
+ return Event.Bus.valueOf(val.toUpperCase());
+ }
+
+ private SparseArray<int[]> readConfiguration()
+ throws IllegalStateException, IOException {
+ SparseArray<int[]> configuration = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ Event.UinputControlCode controlCode = null;
+ IntStream data = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "type":
+ controlCode = readUinputControlCode();
+ break;
+ case "data":
+ Objects.requireNonNull(controlCode,
+ "Configuration 'type' must be specified before 'data'.");
+ data = readDataForControlCode(controlCode)
+ .stream().mapToInt(Integer::intValue);
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException(
+ "Invalid key in device configuration: " + name);
+ }
+ }
+ mReader.endObject();
+ if (controlCode != null && data != null) {
+ final int[] existing = configuration.get(controlCode.getValue());
+ configuration.put(controlCode.getValue(), existing == null ? data.toArray()
+ : IntStream.concat(IntStream.of(existing), data).toArray());
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return configuration;
+ }
+
+ private Event.UinputControlCode readUinputControlCode() throws IOException {
+ var code = readValueAsInt((controlTypeStr) -> {
+ try {
+ return Event.UinputControlCode.valueOf(controlTypeStr).getValue();
+ } catch (IllegalArgumentException ex) {
+ return -1;
+ }
+ });
+ for (Event.UinputControlCode controlCode : Event.UinputControlCode.values()) {
+ if (controlCode.getValue() == code) {
+ return controlCode;
+ }
+ }
+ return null;
+ }
+
+ private List<Integer> readDataForControlCode(
+ Event.UinputControlCode controlCode) throws IOException {
+ return switch (controlCode) {
+ case UI_SET_EVBIT -> readArrayAsInts(this::readEvdevEventType);
+ case UI_SET_KEYBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_KEY));
+ case UI_SET_RELBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_REL));
+ case UI_SET_ABSBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_ABS));
+ case UI_SET_MSCBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_MSC));
+ case UI_SET_LEDBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_LED));
+ case UI_SET_SNDBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_SND));
+ case UI_SET_FFBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_FF));
+ case UI_SET_SWBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_SW));
+ case UI_SET_PROPBIT -> readArrayAsInts(this::readEvdevInputProp);
+ };
+ }
+
+ interface IntValueReader {
+ int readNextValue() throws IOException;
+ }
+
+ private ArrayList<Integer> readArrayAsInts(
+ IntValueReader nextValueReader) throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ data.add(nextValueReader.readNextValue());
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
+ private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
+ InputAbsInfo absInfo = new InputAbsInfo();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "value" -> absInfo.value = readInt();
+ case "minimum" -> absInfo.minimum = readInt();
+ case "maximum" -> absInfo.maximum = readInt();
+ case "fuzz" -> absInfo.fuzz = readInt();
+ case "flat" -> absInfo.flat = readInt();
+ case "resolution" -> absInfo.resolution = readInt();
+ default -> {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info: " + name);
+ }
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return absInfo;
+ }
+
+ private SparseArray<InputAbsInfo> readAbsInfoArray()
+ throws IllegalStateException, IOException {
+ SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ int type = 0;
+ InputAbsInfo absInfo = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "code" -> type = readEvdevEventCode(Event.EV_ABS);
+ case "info" -> absInfo = readAbsInfo();
+ default -> {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info array: "
+ + name);
+ }
+ }
+ }
+ mReader.endObject();
+ if (absInfo != null) {
+ infoArray.put(type, absInfo);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return infoArray;
+ }
+
+ private int readEvdevEventType() throws IOException {
+ return readValueAsInt(Device::getEvdevEventTypeByLabel);
+ }
+
+ private int readEvdevEventCode(int type) throws IOException {
+ return readValueAsInt((str) -> Device.getEvdevEventCodeByLabel(type, str));
+ }
+
+ private int readEvdevInputProp() throws IOException {
+ return readValueAsInt(Device::getEvdevInputPropByLabel);
+ }
+
+ private void consumeRemainingElements() throws IOException {
+ while (mReader.hasNext()) {
+ mReader.skipValue();
+ }
+ }
+
+ private static void error(String msg, Exception e) {
+ System.out.println(msg);
+ Log.e(TAG, msg);
+ if (e != null) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index 47b7a354..fe76abb 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -35,7 +35,7 @@
public class Uinput {
private static final String TAG = "UINPUT";
- private final Event.Reader mReader;
+ private final JsonStyleParser mParser;
private final SparseArray<Device> mDevices;
private static void usage() {
@@ -74,7 +74,7 @@
private Uinput(InputStream in) {
mDevices = new SparseArray<Device>();
try {
- mReader = new Event.Reader(new InputStreamReader(in, "UTF-8"));
+ mParser = new JsonStyleParser(new InputStreamReader(in, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
@@ -83,7 +83,7 @@
private void run() {
try {
Event e = null;
- while ((e = mReader.getNextEvent()) != null) {
+ while ((e = mParser.getNextEvent()) != null) {
process(e);
}
} catch (IOException ex) {
@@ -111,7 +111,7 @@
case REGISTER ->
error("Device id=" + e.getId() + " is already registered. Ignoring event.");
case INJECT -> d.injectEvent(e.getInjections());
- case DELAY -> d.addDelay(e.getDuration());
+ case DELAY -> d.addDelay(e.getDurationMillis());
case SYNC -> d.syncEvent(e.getSyncToken());
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ee1659b..fbd2142 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3331,6 +3331,53 @@
}
+package android.companion.virtual.camera {
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCamera implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig getConfig();
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public interface VirtualCameraCallback {
+ method public void onProcessCaptureRequest(int, long, @Nullable android.companion.virtual.camera.VirtualCameraMetadata);
+ method public void onStreamClosed(int);
+ method public void onStreamConfigured(int, @NonNull android.view.Surface, @NonNull android.companion.virtual.camera.VirtualCameraStreamConfig);
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @StringRes public int getDisplayNameStringRes();
+ method @NonNull public java.util.Set<android.companion.virtual.camera.VirtualCameraStreamConfig> getStreamConfigs();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraConfig> CREATOR;
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final class VirtualCameraConfig.Builder {
+ ctor public VirtualCameraConfig.Builder();
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder addStreamConfig(int, int, int);
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig build();
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setDisplayNameStringRes(@StringRes int);
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setVirtualCameraCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.camera.VirtualCameraCallback);
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraMetadata implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraMetadata> CREATOR;
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraStreamConfig implements android.os.Parcelable {
+ ctor public VirtualCameraStreamConfig(@IntRange(from=1) int, @IntRange(from=1) int, int);
+ method public int describeContents();
+ method public int getFormat();
+ method @IntRange(from=1) public int getHeight();
+ method @IntRange(from=1) public int getWidth();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraStreamConfig> CREATOR;
+ }
+
+}
+
package android.companion.virtual.sensor {
public final class VirtualSensor implements android.os.Parcelable {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fa52968..79a5879 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -113,6 +113,7 @@
import android.hardware.lights.LightsManager;
import android.hardware.lights.SystemLightsManager;
import android.hardware.location.ContextHubManager;
+import android.hardware.location.IContextHubService;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbManager;
@@ -1118,8 +1119,12 @@
new CachedServiceFetcher<ContextHubManager>() {
@Override
public ContextHubManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- return new ContextHubManager(ctx.getOuterContext(),
- ctx.mMainThread.getHandler().getLooper());
+ IBinder b = ServiceManager.getService(Context.CONTEXTHUB_SERVICE);
+ if (b == null) {
+ return null;
+ }
+ return new ContextHubManager(IContextHubService.Stub.asInterface(b),
+ ctx.mMainThread.getHandler().getLooper());
}});
registerService(Context.INCIDENT_SERVICE, IncidentManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1d036a1..3ee9d692 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13400,7 +13400,7 @@
* A device owner, by default, may continue granting these permissions. However, for increased
* user control, the admin may opt out of controlling grants for these permissions by including
* {@link #EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters.
- * In that case the device owner's control will be limited do denying these permissions.
+ * In that case the device owner's control will be limited to denying these permissions.
* <p>
* NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over
* the following permissions are restricted for managed profile owners:
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 2f97080..102cbf3 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -23,8 +23,7 @@
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
+import android.companion.virtual.camera.VirtualCameraConfig;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.graphics.Point;
@@ -236,8 +235,15 @@
void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
/**
- * Creates a new VirtualCamera and registers it with the VirtualCameraProvider.
+ * Creates a new virtual camera and registers it with the virtual camera service.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void registerVirtualCamera(in IVirtualCamera camera);
+ void registerVirtualCamera(in VirtualCameraConfig camera);
+
+ /**
+ * Destroys the virtual camera with given config and unregisters it from the virtual camera
+ * service.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void unregisterVirtualCamera(in VirtualCameraConfig camera);
}
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index 93a3e78..d0c8be6 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -32,9 +32,8 @@
* Details of a particular virtual device.
*
* <p>Read-only device representation exposing the properties of an existing virtual device.
- *
- * @see VirtualDeviceManager#registerVirtualDeviceListener
*/
+// TODO(b/310912420): Link to VirtualDeviceManager#registerVirtualDeviceListener from the docs
public final class VirtualDevice implements Parcelable {
private final @NonNull IVirtualDevice mVirtualDevice;
@@ -92,8 +91,8 @@
* per device.
*
* @see Context#createDeviceContext
- * @see #getPersistentDeviceId
*/
+ // TODO(b/310912420): Link to #getPersistentDeviceId from the docs
public int getDeviceId() {
return mId;
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 60965a8..41c90b9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -212,9 +212,10 @@
* existing virtual devices.</p>
*
* <p>Note that if a virtual device is closed and becomes invalid, the returned objects will
- * not be updated and may contain stale values. Use a {@link VirtualDeviceListener} for real
- * time updates of the availability of virtual devices.</p>
+ * not be updated and may contain stale values.</p>
*/
+ // TODO(b/310912420): Add "Use a VirtualDeviceListener for real time updates of the
+ // availability of virtual devices." in the note paragraph above with a link annotation.
@NonNull
public List<android.companion.virtual.VirtualDevice> getVirtualDevices() {
if (mService == null) {
diff --git a/core/java/android/companion/virtual/camera/IVirtualCamera.aidl b/core/java/android/companion/virtual/camera/IVirtualCamera.aidl
deleted file mode 100644
index 58b850d..0000000
--- a/core/java/android/companion/virtual/camera/IVirtualCamera.aidl
+++ /dev/null
@@ -1,17 +0,0 @@
-package android.companion.virtual.camera;
-
-import android.companion.virtual.camera.IVirtualCameraSession;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-
-/**
- * Counterpart of ICameraDevice for virtual camera.
- *
- * @hide
- */
-interface IVirtualCamera {
-
- IVirtualCameraSession open();
-
- VirtualCameraHalConfig getHalConfig();
-
-}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
new file mode 100644
index 0000000..fac44b5
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
@@ -0,0 +1,70 @@
+/*
+ * 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.virtual.camera;
+
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.companion.virtual.camera.VirtualCameraMetadata;
+import android.view.Surface;
+
+/**
+ * Interface for the virtual camera service and system server to talk back to the virtual camera owner.
+ *
+ * @hide
+ */
+interface IVirtualCameraCallback {
+
+ /**
+ * Called when one of the requested stream has been configured by the virtual camera service and
+ * is ready to receive data onto its {@link Surface}
+ *
+ * @param streamId The id of the configured stream
+ * @param surface The surface to write data into for this stream
+ * @param streamConfig The image data configuration for this stream
+ */
+ oneway void onStreamConfigured(
+ int streamId,
+ in Surface surface,
+ in VirtualCameraStreamConfig streamConfig);
+
+ /**
+ * The client application is requesting a camera frame for the given streamId with the provided
+ * metadata.
+ *
+ * <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
+ * this stream that was provided during the {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} call.
+ *
+ * @param streamId The streamId for which the frame is requested. This corresponds to the
+ * streamId that was given in {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)}
+ * @param frameId The frameId that is being requested. Each request will have a different
+ * frameId, that will be increasing for each call with a particular streamId.
+ * @param metadata The metadata requested for the frame. The virtual camera should do its best
+ * to honor the requested metadata.
+ */
+ oneway void onProcessCaptureRequest(
+ int streamId, long frameId, in VirtualCameraMetadata metadata);
+
+ /**
+ * The stream previously configured when {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} was called is now being closed and associated resources can be
+ * freed. The Surface was disposed on the client side and should not be used anymore by the virtual camera owner
+ *
+ * @param streamId The id of the stream that was closed.
+ */
+ oneway void onStreamClosed(int streamId);
+
+}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java
index 791bf0a..beee86f 100644
--- a/core/java/android/companion/virtual/camera/VirtualCamera.java
+++ b/core/java/android/companion/virtual/camera/VirtualCamera.java
@@ -16,20 +16,44 @@
package android.companion.virtual.camera;
+import android.annotation.FlaggedApi;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
+import android.companion.virtual.flags.Flags;
+import android.hardware.camera2.CameraDevice;
import android.os.RemoteException;
import androidx.annotation.NonNull;
+import java.io.Closeable;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
- * Virtual camera that is used to send image data into system.
+ * A VirtualCamera is the representation of a remote or computer generated camera that will be
+ * exposed to applications using the Android Camera APIs.
*
+ * <p>A VirtualCamera is created using {@link
+ * VirtualDeviceManager.VirtualDevice#createVirtualCamera(VirtualCameraConfig)}.
+ *
+ * <p>Once a virtual camera is created, it will receive callbacks from the system when an
+ * application attempts to use it via the {@link VirtualCameraCallback} class set using {@link
+ * VirtualCameraConfig.Builder#setVirtualCameraCallback(Executor, VirtualCameraCallback)}
+ *
+ * @see VirtualDeviceManager.VirtualDevice#createVirtualDevice(int, VirtualDeviceParams)
+ * @see VirtualCameraConfig.Builder#setVirtualCameraCallback(Executor, VirtualCameraCallback)
+ * @see android.hardware.camera2.CameraManager#openCamera(String, CameraDevice.StateCallback,
+ * android.os.Handler)
* @hide
*/
-public final class VirtualCamera extends IVirtualCamera.Stub {
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCamera implements Closeable {
+ private final IVirtualDevice mVirtualDevice;
private final VirtualCameraConfig mConfig;
/**
@@ -37,40 +61,35 @@
*
* @param virtualDevice The Binder object representing this camera in the server.
* @param config Configuration for the new virtual camera
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public VirtualCamera(
@NonNull IVirtualDevice virtualDevice, @NonNull VirtualCameraConfig config) {
+ mVirtualDevice = virtualDevice;
mConfig = Objects.requireNonNull(config);
Objects.requireNonNull(virtualDevice);
+ // TODO(b/310857519): Avoid registration inside constructor.
try {
- virtualDevice.registerVirtualCamera(this);
+ mVirtualDevice.registerVirtualCamera(config);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
- /** Get the camera session associated with this device */
- @Override
- public IVirtualCameraSession open() {
- // TODO: b/302255544 - Make this async.
- VirtualCameraSession session = mConfig.getCallback().onOpenSession();
- return new VirtualCameraSessionInternal(session);
- }
-
/** Returns the configuration of this virtual camera instance. */
@NonNull
public VirtualCameraConfig getConfig() {
return mConfig;
}
- /**
- * Returns the configuration to be used by the virtual camera HAL.
- *
- * @hide
- */
@Override
- @NonNull
- public VirtualCameraHalConfig getHalConfig() {
- return mConfig.getHalConfig();
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void close() {
+ try {
+ mVirtualDevice.unregisterVirtualCamera(mConfig);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
index a7c3d4f..a18ae03 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
@@ -16,7 +16,12 @@
package android.companion.virtual.camera;
-import android.hardware.camera2.params.SessionConfiguration;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.view.Surface;
import java.util.concurrent.Executor;
@@ -24,15 +29,53 @@
* Interface to be provided when creating a new {@link VirtualCamera} in order to receive callbacks
* from the framework and the camera system.
*
- * @see VirtualCameraConfig.Builder#setCallback(Executor, VirtualCameraCallback)
+ * @see VirtualCameraConfig.Builder#setVirtualCameraCallback(Executor, VirtualCameraCallback)
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public interface VirtualCameraCallback {
/**
- * Called when a client opens a new camera session for the associated {@link VirtualCamera}
+ * Called when one of the requested stream has been configured by the virtual camera service and
+ * is ready to receive data onto its {@link Surface}
*
- * @see android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)
+ * @param streamId The id of the configured stream
+ * @param surface The surface to write data into for this stream
+ * @param streamConfig The image data configuration for this stream
*/
- VirtualCameraSession onOpenSession();
+ void onStreamConfigured(
+ int streamId,
+ @NonNull Surface surface,
+ @NonNull VirtualCameraStreamConfig streamConfig);
+
+ /**
+ * The client application is requesting a camera frame for the given streamId with the provided
+ * metadata.
+ *
+ * <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
+ * this stream that was provided during the {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} call.
+ *
+ * @param streamId The streamId for which the frame is requested. This corresponds to the
+ * streamId that was given in {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)}
+ * @param frameId The frameId that is being requested. Each request will have a different
+ * frameId, that will be increasing for each call with a particular streamId.
+ * @param metadata The metadata requested for the frame. The virtual camera should do its best
+ * to honor the requested metadata but the consumer won't be informed about the metadata set
+ * for a particular frame. If null, the requested frame can be anything the producer sends.
+ */
+ void onProcessCaptureRequest(
+ int streamId, long frameId, @Nullable VirtualCameraMetadata metadata);
+
+ /**
+ * The stream previously configured when {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} was called is now being closed and associated resources can be
+ * freed. The Surface corresponding to that streamId was disposed on the client side and should
+ * not be used anymore by the virtual camera owner
+ *
+ * @param streamId The id of the stream that was closed.
+ */
+ void onStreamClosed(int streamId);
}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/core/java/android/companion/virtual/camera/VirtualCameraConfig.aidl
similarity index 76%
copy from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
copy to core/java/android/companion/virtual/camera/VirtualCameraConfig.aidl
index 2529807..88c27a5 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.aidl
@@ -15,14 +15,5 @@
*/
package android.companion.virtual.camera;
-/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
- * @hide
- */
-interface IVirtualCameraSession {
-
- void configureStream(int width, int height, int format);
-
- void close();
-}
+/** @hide */
+parcelable VirtualCameraConfig;
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
index fb464d5..f1eb240 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
@@ -18,11 +18,19 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.content.res.Resources;
import android.graphics.ImageFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArraySet;
+import android.view.Surface;
-import java.util.Collections;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -33,33 +41,115 @@
*
* @hide
*/
-public final class VirtualCameraConfig {
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCameraConfig implements Parcelable {
- private final String mDisplayName;
+ private final @StringRes int mNameStringRes;
private final Set<VirtualCameraStreamConfig> mStreamConfigurations;
- private final VirtualCameraCallback mCallback;
- private final Executor mCallbackExecutor;
+ private final IVirtualCameraCallback mCallback;
+
+ private VirtualCameraConfig(
+ int displayNameStringRes,
+ @NonNull Set<VirtualCameraStreamConfig> streamConfigurations,
+ @NonNull Executor executor,
+ @NonNull VirtualCameraCallback callback) {
+ mNameStringRes = displayNameStringRes;
+ mStreamConfigurations =
+ Set.copyOf(requireNonNull(streamConfigurations, "Missing stream configurations"));
+ if (mStreamConfigurations.isEmpty()) {
+ throw new IllegalArgumentException(
+ "At least one stream configuration is needed to create a virtual camera.");
+ }
+ mCallback =
+ new VirtualCameraCallbackInternal(
+ requireNonNull(callback, "Missing callback"),
+ requireNonNull(executor, "Missing callback executor"));
+ }
+
+ private VirtualCameraConfig(@NonNull Parcel in) {
+ mNameStringRes = in.readInt();
+ mCallback = IVirtualCameraCallback.Stub.asInterface(in.readStrongBinder());
+ mStreamConfigurations =
+ Set.of(
+ in.readParcelableArray(
+ VirtualCameraStreamConfig.class.getClassLoader(),
+ VirtualCameraStreamConfig.class));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mNameStringRes);
+ dest.writeStrongInterface(mCallback);
+ dest.writeParcelableArray(
+ mStreamConfigurations.toArray(new VirtualCameraStreamConfig[0]), flags);
+ }
+
+ /**
+ * @return The display name of this VirtualCamera
+ */
+ @StringRes
+ public int getDisplayNameStringRes() {
+ return mNameStringRes;
+ }
+
+ /**
+ * Returns an unmodifiable set of the stream configurations added to this {@link
+ * VirtualCameraConfig}.
+ *
+ * @see VirtualCameraConfig.Builder#addStreamConfig(int, int, int)
+ */
+ @NonNull
+ public Set<VirtualCameraStreamConfig> getStreamConfigs() {
+ return mStreamConfigurations;
+ }
+
+ /**
+ * Returns the callback used to communicate from the server to the client.
+ *
+ * @hide
+ */
+ @NonNull
+ public IVirtualCameraCallback getCallback() {
+ return mCallback;
+ }
/**
* Builder for {@link VirtualCameraConfig}.
*
* <p>To build an instance of {@link VirtualCameraConfig} the following conditions must be met:
- * <li>At least one stream must be added wit {@link #addStreamConfiguration(int, int, int)}.
- * <li>A name must be set with {@link #setDisplayName(String)}
- * <li>A callback must be set wit {@link #setCallback(Executor, VirtualCameraCallback)}
+ * <li>At least one stream must be added with {@link #addStreamConfig(int, int, int)}.
+ * <li>A callback must be set with {@link #setVirtualCameraCallback(Executor,
+ * VirtualCameraCallback)}
+ * <li>A user readable name can be set with {@link #setDisplayNameStringRes(int)}
*/
+ @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public static final class Builder {
- private String mDisplayName;
- private final ArraySet<VirtualCameraStreamConfig> mStreamConfiguration = new ArraySet<>();
+ private @StringRes int mDisplayNameStringRes = Resources.ID_NULL;
+ private final ArraySet<VirtualCameraStreamConfig> mStreamConfigurations = new ArraySet<>();
private Executor mCallbackExecutor;
private VirtualCameraCallback mCallback;
- /** Set the visible name of this camera for the user. */
- // TODO: b/290172356 - Take a resource id instead of displayName
+ /**
+ * Set the visible name of this camera for the user.
+ *
+ * <p>Sets the resource to a string representing a user readable name for this virtual
+ * camera.
+ *
+ * @throws IllegalArgumentException if an invalid resource id is passed.
+ */
@NonNull
- public Builder setDisplayName(@NonNull String displayName) {
- mDisplayName = requireNonNull(displayName);
+ public Builder setDisplayNameStringRes(@StringRes int displayNameStringRes) {
+ if (displayNameStringRes <= 0) {
+ throw new IllegalArgumentException("Invalid resource passed for display name");
+ }
+ mDisplayNameStringRes = displayNameStringRes;
return this;
}
@@ -68,18 +158,21 @@
*
* <p>At least one {@link VirtualCameraStreamConfig} must be added.
*
- * @param width The width of the stream
- * @param height The height of the stream
- * @param format The {@link ImageFormat} of the stream
+ * @param width The width of the stream.
+ * @param height The height of the stream.
+ * @param format The {@link ImageFormat} of the stream.
+ *
+ * @throws IllegalArgumentException if invalid format or dimensions are passed.
*/
@NonNull
- public Builder addStreamConfiguration(
- int width, int height, @ImageFormat.Format int format) {
- VirtualCameraStreamConfig streamConfig = new VirtualCameraStreamConfig();
- streamConfig.width = width;
- streamConfig.height = height;
- streamConfig.format = format;
- mStreamConfiguration.add(streamConfig);
+ public Builder addStreamConfig(int width, int height, @ImageFormat.Format int format) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Invalid dimensions passed for stream config");
+ }
+ if (!ImageFormat.isPublicFormat(format)) {
+ throw new IllegalArgumentException("Invalid format passed for stream config");
+ }
+ mStreamConfigurations.add(new VirtualCameraStreamConfig(width, height, format));
return this;
}
@@ -93,7 +186,9 @@
* @param callback The instance of the callback to be added. Subsequent call to this method
* will replace the callback set.
*/
- public Builder setCallback(
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder") // The configuration is immutable
+ public Builder setVirtualCameraCallback(
@NonNull Executor executor, @NonNull VirtualCameraCallback callback) {
mCallbackExecutor = requireNonNull(executor);
mCallback = requireNonNull(callback);
@@ -108,67 +203,49 @@
@NonNull
public VirtualCameraConfig build() {
return new VirtualCameraConfig(
- mDisplayName, mStreamConfiguration, mCallbackExecutor, mCallback);
+ mDisplayNameStringRes, mStreamConfigurations, mCallbackExecutor, mCallback);
}
}
- private VirtualCameraConfig(
- @NonNull String displayName,
- @NonNull Set<VirtualCameraStreamConfig> streamConfigurations,
- @NonNull Executor executor,
- @NonNull VirtualCameraCallback callback) {
- mDisplayName = requireNonNull(displayName, "Missing display name");
- mStreamConfigurations =
- Collections.unmodifiableSet(
- requireNonNull(streamConfigurations, "Missing stream configuration"));
- if (mStreamConfigurations.isEmpty()) {
- throw new IllegalArgumentException(
- "At least one StreamConfiguration is needed to create a virtual camera.");
+ private static class VirtualCameraCallbackInternal extends IVirtualCameraCallback.Stub {
+
+ private final VirtualCameraCallback mCallback;
+ private final Executor mExecutor;
+
+ private VirtualCameraCallbackInternal(VirtualCameraCallback callback, Executor executor) {
+ mCallback = callback;
+ mExecutor = executor;
}
- mCallback = requireNonNull(callback, "Missing callback");
- mCallbackExecutor = requireNonNull(executor, "Missing callback executor");
+
+ @Override
+ public void onStreamConfigured(
+ int streamId, Surface surface, VirtualCameraStreamConfig streamConfig) {
+ mExecutor.execute(() -> mCallback.onStreamConfigured(streamId, surface, streamConfig));
+ }
+
+ @Override
+ public void onProcessCaptureRequest(
+ int streamId, long frameId, VirtualCameraMetadata metadata) {
+ mExecutor.execute(() -> mCallback.onProcessCaptureRequest(streamId, frameId, metadata));
+ }
+
+ @Override
+ public void onStreamClosed(int streamId) {
+ mExecutor.execute(() -> mCallback.onStreamClosed(streamId));
+ }
}
- /**
- * @return The display name of this VirtualCamera
- */
@NonNull
- public String getDisplayName() {
- return mDisplayName;
- }
+ public static final Parcelable.Creator<VirtualCameraConfig> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public VirtualCameraConfig createFromParcel(Parcel in) {
+ return new VirtualCameraConfig(in);
+ }
- /**
- * Returns an unmodifiable set of the stream configurations added to this {@link
- * VirtualCameraConfig}.
- *
- * @see VirtualCameraConfig.Builder#addStreamConfiguration(int, int, int)
- */
- @NonNull
- public Set<VirtualCameraStreamConfig> getStreamConfigs() {
- return mStreamConfigurations;
- }
-
- /** Returns the callback used to communicate from the server to the client. */
- @NonNull
- public VirtualCameraCallback getCallback() {
- return mCallback;
- }
-
- /** Returns the executor onto which the callback should be run. */
- @NonNull
- public Executor getCallbackExecutor() {
- return mCallbackExecutor;
- }
-
- /**
- * Returns a new instance of {@link VirtualCameraHalConfig} initialized with data from this
- * {@link VirtualCameraConfig}
- */
- @NonNull
- public VirtualCameraHalConfig getHalConfig() {
- VirtualCameraHalConfig halConfig = new VirtualCameraHalConfig();
- halConfig.displayName = mDisplayName;
- halConfig.streamConfigs = mStreamConfigurations.toArray(new VirtualCameraStreamConfig[0]);
- return halConfig;
- }
+ @Override
+ public VirtualCameraConfig[] newArray(int size) {
+ return new VirtualCameraConfig[size];
+ }
+ };
}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl b/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl
deleted file mode 100644
index 7070a38..0000000
--- a/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl
+++ /dev/null
@@ -1,12 +0,0 @@
-package android.companion.virtual.camera;
-
-import android.companion.virtual.camera.VirtualCameraStreamConfig;
-
-/**
- * Configuration for VirtualCamera to be passed to the server and HAL service.
- * @hide
- */
-parcelable VirtualCameraHalConfig {
- String displayName;
- VirtualCameraStreamConfig[] streamConfigs;
-}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
similarity index 79%
rename from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
rename to core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
index 2529807..6c1f0fc 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
@@ -13,16 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.companion.virtual.camera;
/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
+ * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
+ * VirtualCamera.
* @hide
*/
-interface IVirtualCameraSession {
-
- void configureStream(int width, int height, int format);
-
- void close();
-}
+parcelable VirtualCameraMetadata;
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
new file mode 100644
index 0000000..1ba36d0
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
@@ -0,0 +1,61 @@
+/*
+ * 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.virtual.camera;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data structure used to store camera metadata compatible with VirtualCamera.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCameraMetadata implements Parcelable {
+
+ /** @hide */
+ public VirtualCameraMetadata(@NonNull Parcel in) {}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+ @NonNull
+ public static final Creator<VirtualCameraMetadata> CREATOR =
+ new Creator<>() {
+ @Override
+ @NonNull
+ public VirtualCameraMetadata createFromParcel(Parcel in) {
+ return new VirtualCameraMetadata(in);
+ }
+
+ @Override
+ @NonNull
+ public VirtualCameraMetadata[] newArray(int size) {
+ return new VirtualCameraMetadata[size];
+ }
+ };
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraSession.java b/core/java/android/companion/virtual/camera/VirtualCameraSession.java
deleted file mode 100644
index c25d977..0000000
--- a/core/java/android/companion/virtual/camera/VirtualCameraSession.java
+++ /dev/null
@@ -1,30 +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.virtual.camera;
-
-/***
- * Counterpart of {@link android.hardware.camera2.CameraCaptureSession} for producing
- * images from a {@link VirtualCamera}.
- * @hide
- */
-// TODO: b/289881985 - This is just a POC implementation for now, this will be extended
-// to a full featured Camera Session
-public interface VirtualCameraSession {
-
- /** Close the session and release its resources. */
- default void close() {}
-}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java b/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java
deleted file mode 100644
index da168de..0000000
--- a/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java
+++ /dev/null
@@ -1,44 +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.virtual.camera;
-
-import android.graphics.ImageFormat;
-
-import androidx.annotation.NonNull;
-
-import java.util.Objects;
-
-/**
- * Wraps the client side {@link VirtualCameraSession} into an {@link IVirtualCameraSession}.
- *
- * @hide
- */
-final class VirtualCameraSessionInternal extends IVirtualCameraSession.Stub {
-
- @SuppressWarnings("FieldCanBeLocal")
- // TODO: b/289881985: Will be used once connected with the CameraService
- private final VirtualCameraSession mVirtualCameraSession;
-
- VirtualCameraSessionInternal(@NonNull VirtualCameraSession virtualCameraSession) {
- mVirtualCameraSession = Objects.requireNonNull(virtualCameraSession);
- }
-
- @Override
- public void configureStream(int width, int height, @ImageFormat.Format int format) {}
-
- public void close() {}
-}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
index 304d455..ce92b6d 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
+++ b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
@@ -16,11 +16,7 @@
package android.companion.virtual.camera;
/**
- * A stream configuration supported by a virtual camera
+ * The configuration of a single virtual camera stream.
* @hide
*/
-parcelable VirtualCameraStreamConfig {
- int width;
- int height;
- int format;
-}
+parcelable VirtualCameraStreamConfig;
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java
new file mode 100644
index 0000000..e198821
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java
@@ -0,0 +1,106 @@
+/*
+ * 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.virtual.camera;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.graphics.ImageFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The configuration of a single virtual camera stream.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCameraStreamConfig implements Parcelable {
+
+ private final int mWidth;
+ private final int mHeight;
+ private final int mFormat;
+
+ /**
+ * Construct a new instance of {@link VirtualCameraStreamConfig} initialized with the provided
+ * width, height and {@link ImageFormat}
+ *
+ * @param width The width of the stream.
+ * @param height The height of the stream.
+ * @param format The {@link ImageFormat} of the stream.
+ */
+ public VirtualCameraStreamConfig(
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @ImageFormat.Format int format) {
+ this.mWidth = width;
+ this.mHeight = height;
+ this.mFormat = format;
+ }
+
+ private VirtualCameraStreamConfig(@NonNull Parcel in) {
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mFormat = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeInt(mFormat);
+ }
+
+ @NonNull
+ public static final Creator<VirtualCameraStreamConfig> CREATOR =
+ new Creator<>() {
+ @Override
+ public VirtualCameraStreamConfig createFromParcel(Parcel in) {
+ return new VirtualCameraStreamConfig(in);
+ }
+
+ @Override
+ public VirtualCameraStreamConfig[] newArray(int size) {
+ return new VirtualCameraStreamConfig[size];
+ }
+ };
+
+ /** Returns the width of this stream. */
+ @IntRange(from = 1)
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /** Returns the height of this stream. */
+ @IntRange(from = 1)
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /** Returns the {@link ImageFormat} of this stream. */
+ @ImageFormat.Format
+ public int getFormat() {
+ return mFormat;
+ }
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 01ce7b9..481ec72 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -15,6 +15,8 @@
*/
package android.hardware.location;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -1111,12 +1113,12 @@
}
};
- /** @throws ServiceNotFoundException
- * @hide */
- public ContextHubManager(Context context, Looper mainLooper) throws ServiceNotFoundException {
+ /** @hide */
+ public ContextHubManager(@NonNull IContextHubService service, @NonNull Looper mainLooper) {
+ requireNonNull(service, "service cannot be null");
+ requireNonNull(mainLooper, "mainLooper cannot be null");
+ mService = service;
mMainLooper = mainLooper;
- mService = IContextHubService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.CONTEXTHUB_SERVICE));
try {
mService.registerCallback(mClientCallback);
} catch (RemoteException e) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4e7734c..2e82ce2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -19472,6 +19472,7 @@
*
* @hide
*/
+ @Readable
public static final String WEAR_MEDIA_CONTROLS_PACKAGE = "wear_media_controls_package";
/**
@@ -19479,6 +19480,7 @@
*
* @hide
*/
+ @Readable
public static final String WEAR_MEDIA_SESSIONS_PACKAGE = "wear_media_sessions_package";
/*
diff --git a/packages/SettingsLib/Spa/screenshot/Android.bp b/packages/SettingsLib/Spa/screenshot/Android.bp
index bd508cb..dbf4ce0 100644
--- a/packages/SettingsLib/Spa/screenshot/Android.bp
+++ b/packages/SettingsLib/Spa/screenshot/Android.bp
@@ -18,6 +18,14 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+filegroup {
+ name: "SpaScreenshotTestRNGFiles",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+}
+
android_test {
name: "SpaScreenshotTests",
use_resource_processor: true,
@@ -36,6 +44,7 @@
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
+ "platform-parametric-runner-lib",
"platform-screenshot-diff-core",
],
kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
new file mode 100644
index 0000000..6b8197c
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
@@ -0,0 +1,74 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+ name: "SpaRoboRNGTestsAssetsLib",
+ asset_dirs: ["assets"],
+ sdk_version: "current",
+ platform_apis: true,
+ manifest: "AndroidManifest.xml",
+ optimize: {
+ enabled: false,
+ },
+ use_resource_processor: true,
+ resource_dirs: ["res"],
+}
+
+android_app {
+ name: "SpaRoboApp",
+ srcs: [],
+ static_libs: [
+ "androidx.test.espresso.core",
+ "androidx.appcompat_appcompat",
+ "flag-junit",
+ "guava",
+ "SpaLib",
+ "SpaLibTestUtils",
+ "SpaRoboRNGTestsAssetsLib",
+ "platform-screenshot-diff-core",
+ "PlatformComposeSceneTransitionLayoutTestsUtils",
+ ],
+ manifest: "robo-manifest.xml",
+ aaptflags: [
+ "--extra-packages",
+ "com.android.settingslib.spa.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: "SpaRoboRNGTests",
+ srcs: [
+ ":SpaScreenshotTestRNGFiles",
+ ":flag-junit",
+ ":platform-test-screenshot-rules",
+ ],
+ // Do not add any new libraries here, they should be added to SpaRoboApp 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: "SpaRoboApp",
+}
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/AndroidManifest.xml b/packages/SettingsLib/Spa/screenshot/robotests/AndroidManifest.xml
new file mode 100644
index 0000000..1918e1c
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spa.screenshot">
+
+ <uses-sdk android:minSdkVersion="21"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+</manifest>
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png
new file mode 100644
index 0000000..b2f3cf1
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_barChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_barChart.png
new file mode 100644
index 0000000..dc7e756
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_barChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_footer.png
new file mode 100644
index 0000000..95021fa
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_footer.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_imageIllustration.png
new file mode 100644
index 0000000..281f572
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_lineChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_lineChart.png
new file mode 100644
index 0000000..0dc707f
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_lineChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
new file mode 100644
index 0000000..7c135a0
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_pieChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_pieChart.png
new file mode 100644
index 0000000..7c3e993
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_pieChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png
new file mode 100644
index 0000000..7b438c4
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png
new file mode 100644
index 0000000..ac64619
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
new file mode 100644
index 0000000..5506c8c
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
new file mode 100644
index 0000000..bb518c0
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png
new file mode 100644
index 0000000..f4b9063
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png
new file mode 100644
index 0000000..fc60c0f
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png
new file mode 100644
index 0000000..fa06927
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_barChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_barChart.png
new file mode 100644
index 0000000..698cb4e
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_barChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_footer.png
new file mode 100644
index 0000000..95021fa
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_footer.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png
new file mode 100644
index 0000000..a5f3fa5
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_lineChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_lineChart.png
new file mode 100644
index 0000000..c1938b4
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_lineChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
new file mode 100644
index 0000000..81a181f
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_pieChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_pieChart.png
new file mode 100644
index 0000000..ffc6729
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_pieChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png
new file mode 100644
index 0000000..a620040
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png
new file mode 100644
index 0000000..0b40aa8
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
new file mode 100644
index 0000000..cfe8587
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
new file mode 100644
index 0000000..bb518c0
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png
new file mode 100644
index 0000000..3632755
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png
new file mode 100644
index 0000000..7e5b602
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png
new file mode 100644
index 0000000..a3692cd
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png
new file mode 100644
index 0000000..233d088
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
new file mode 100644
index 0000000..10869f2
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png
new file mode 100644
index 0000000..3eaecc1
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png
new file mode 100644
index 0000000..ca61911
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
new file mode 100644
index 0000000..8a0da31
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png
new file mode 100644
index 0000000..19d0afd
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
new file mode 100644
index 0000000..236a1a0
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
new file mode 100644
index 0000000..72b7954
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
new file mode 100644
index 0000000..36486c4
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png
new file mode 100644
index 0000000..5bb318f
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
new file mode 100644
index 0000000..fedce44
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
new file mode 100644
index 0000000..3b389d7
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/config/robolectric.properties b/packages/SettingsLib/Spa/screenshot/robotests/config/robolectric.properties
new file mode 100644
index 0000000..83d7549
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/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/packages/SettingsLib/Spa/screenshot/robotests/res/drawable/accessibility_captioning_banner.xml b/packages/SettingsLib/Spa/screenshot/robotests/res/drawable/accessibility_captioning_banner.xml
new file mode 100644
index 0000000..6597ffb
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/res/drawable/accessibility_captioning_banner.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="412dp"
+ android:height="300dp"
+ android:viewportWidth="412"
+ android:viewportHeight="300">
+ <path
+ android:pathData="M383.9,300H28.1C12.6,300 0,287.4 0,271.9V28.1C0,12.6 12.6,0 28.1,0h355.8C399.4,0 412,12.6 412,28.1v243.8C412,287.4 399.4,300 383.9,300z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M79.2,179.6h53.6v8.5h-53.6z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.5,179.6h30.4v8.5h-30.4z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M79.2,195.5h79.2v8.5h-79.2z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M168.1,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M211.9,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M182.7,179.6h73.1v8.5h-73.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M265.5,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M302.1,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.7,67.9h-11.5c-1.6,0 -2.9,1.3 -2.9,2.9H67.8c-7.9,0 -14.4,6.5 -14.4,14.4v132.4c0,7.9 6.5,14.4 14.4,14.4h276.4c7.9,0 14.4,-6.5 14.4,-14.4V85.2c0,-7.9 -6.5,-14.4 -14.4,-14.4H203.1c0,-1.6 -1.3,-2.9 -2.9,-2.9h-28.8c-1.6,0 -2.9,1.3 -2.9,2.9h-23C145.5,69.2 144.3,67.9 142.7,67.9zM344.2,73.7c6.4,0 11.5,5.2 11.5,11.5v132.4c0,6.3 -5.2,11.5 -11.5,11.5H67.8c-6.4,0 -11.5,-5.2 -11.5,-11.5V85.2c0,-6.3 5.2,-11.5 11.5,-11.5H344.2z"
+ android:fillColor="#DADCE0"/>
+</vector>
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/robo-manifest.xml b/packages/SettingsLib/Spa/screenshot/robotests/robo-manifest.xml
new file mode 100644
index 0000000..af1a11e
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/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.settingslib.spa.screenshot"
+ coreApp="true">
+ <application>
+ <activity
+ android:name="androidx.activity.ComponentActivity"
+ android:exported="true">
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
index 3dcefe9..1cbdc33 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.spa.screenshot.util
+import android.os.Build
import androidx.activity.ComponentActivity
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -49,15 +50,19 @@
)
)
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)
}
/**
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
index b74a243..2cb6044 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
@@ -26,15 +26,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class ActionButtonsScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/package-info.java
similarity index 71%
copy from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
copy to packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/package-info.java
index 2529807..8e55695 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/package-info.java
@@ -13,16 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion.virtual.camera;
-/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
- * @hide
- */
-interface IVirtualCameraSession {
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.button;
- void configureStream(int width, int height, int format);
-
- void close();
-}
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
index 051ef77..7ef9f10 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
@@ -25,15 +25,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class BarChartScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
index 3822571..3790164 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
@@ -25,15 +25,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class LineChartScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
index 6dd62ec..3c3cc85 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
@@ -23,15 +23,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class PieChartScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/package-info.java
similarity index 71%
copy from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
copy to packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/package-info.java
index 2529807..afe3f07 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/package-info.java
@@ -13,16 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion.virtual.camera;
-/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
- * @hide
- */
-interface IVirtualCameraSession {
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.chart;
- void configureStream(int width, int height, int format);
-
- void close();
-}
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
index 0ccfc0b..616b225 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
@@ -24,15 +24,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class ImageIllustrationScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/package-info.java
similarity index 71%
copy from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
copy to packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/package-info.java
index 2529807..0089c2e 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/package-info.java
@@ -13,16 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion.virtual.camera;
-/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
- * @hide
- */
-interface IVirtualCameraSession {
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.illustration;
- void configureStream(int width, int height, int format);
-
- void close();
-}
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
index e547d26..8dd4ce7 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
@@ -23,15 +23,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class MainSwitchPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
index dd6b553..1e1a785 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
@@ -28,15 +28,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class PreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
private const val TITLE = "Title"
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
index 357d815..d1878a74 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
@@ -29,15 +29,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class ProgressBarPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
index fdee7ee..c9f098b 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
@@ -25,15 +25,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class SliderPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
index 6966a74..eca40fb 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
@@ -27,15 +27,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class SwitchPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
index 72c5cb8..f81a59f 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
@@ -24,15 +24,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class TwoTargetSwitchPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/package-info.java
similarity index 71%
copy from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
copy to packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/package-info.java
index 2529807..fd6a5dd 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/package-info.java
@@ -13,16 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion.virtual.camera;
-/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
- * @hide
- */
-interface IVirtualCameraSession {
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.preference;
- void configureStream(int width, int height, int format);
-
- void close();
-}
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
index fb01f77..98a4288 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
@@ -21,15 +21,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class FooterScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
index 2867741..5417095 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
@@ -22,15 +22,16 @@
import org.junit.Rule
import org.junit.Test
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
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class SpinnerScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/package-info.java
similarity index 71%
copy from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
copy to packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/package-info.java
index 2529807..45210ab 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/package-info.java
@@ -13,16 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion.virtual.camera;
-/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
- * @hide
- */
-interface IVirtualCameraSession {
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.ui;
- void configureStream(int width, int height, int format);
-
- void close();
-}
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 80fd516..0be9f84 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -294,6 +294,8 @@
"tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
+ "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
+ "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
// Keyguard helper
@@ -616,6 +618,9 @@
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
+ plugins: [
+ "dagger2-compiler",
+ ],
}
// Opt-out config for optimizing the SystemUI target using R8.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1f2621d..1dcc540 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -59,6 +59,7 @@
import com.android.systemui.keyboard.KeyboardModule;
import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -190,6 +191,7 @@
KeyEventRepositoryModule.class,
KeyboardModule.class,
KeyguardBlueprintModule.class,
+ KeyguardSectionsModule.class,
LetterboxModule.class,
LogModule.class,
MediaProjectionModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 8d5d73f..5c76be8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -49,8 +49,10 @@
* [TransitionInteractor]. These interactors will call [startTransition] and [updateTransition] on
* this repository.
*
- * To print all transitions to logcat to help with debugging, run this command: adb shell settings
- * put global systemui/buffer/KeyguardLog VERBOSE
+ * To print all transitions to logcat to help with debugging, run this command:
+ * ```
+ * adb shell cmd statusbar echo -b KeyguardLog:VERBOSE
+ * ```
*
* This will print all keyguard transitions to logcat with the KeyguardTransitionAuditLogger tag.
*/
@@ -175,9 +177,11 @@
override fun onAnimationStart(animation: Animator) {
emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
}
+
override fun onAnimationCancel(animation: Animator) {
endAnimation(lastStep.value, TransitionState.CANCELED)
}
+
override fun onAnimationEnd(animation: Animator) {
endAnimation(1f, TransitionState.FINISHED)
}
@@ -222,7 +226,7 @@
}
private fun emitTransition(nextStep: TransitionStep, isManual: Boolean = false) {
- trace(nextStep, isManual)
+ logAndTrace(nextStep, isManual)
val emitted = _transitions.tryEmit(nextStep)
if (!emitted) {
Log.w(TAG, "Failed to emit next value without suspending")
@@ -230,26 +234,22 @@
lastStep = nextStep
}
- private fun trace(step: TransitionStep, isManual: Boolean) {
+ private fun logAndTrace(step: TransitionStep, isManual: Boolean) {
if (step.transitionState == TransitionState.RUNNING) {
return
}
- val traceName =
- "Transition: ${step.from} -> ${step.to} " +
- if (isManual) {
- "(manual)"
- } else {
- ""
- }
+ val manualStr = if (isManual) " (manual)" else ""
+ val traceName = "Transition: ${step.from} -> ${step.to}$manualStr"
+
val traceCookie = traceName.hashCode()
- if (step.transitionState == TransitionState.STARTED) {
- Trace.beginAsyncSection(traceName, traceCookie)
- } else if (
- step.transitionState == TransitionState.FINISHED ||
- step.transitionState == TransitionState.CANCELED
- ) {
- Trace.endAsyncSection(traceName, traceCookie)
+ when (step.transitionState) {
+ TransitionState.STARTED -> Trace.beginAsyncSection(traceName, traceCookie)
+ TransitionState.FINISHED -> Trace.endAsyncSection(traceName, traceCookie)
+ TransitionState.CANCELED -> Trace.endAsyncSection(traceName, traceCookie)
+ else -> {}
}
+
+ Log.i(TAG, "${step.transitionState.name} transition: $step$manualStr")
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 419524fe..a03fa38 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -75,24 +75,6 @@
}
scope.launch {
- interactor.finishedKeyguardTransitionStep.collect {
- logger.log(TAG, VERBOSE, "Finished transition", it)
- }
- }
-
- scope.launch {
- interactor.canceledKeyguardTransitionStep.collect {
- logger.log(TAG, VERBOSE, "Canceled transition", it)
- }
- }
-
- scope.launch {
- interactor.startedKeyguardTransitionStep.collect {
- logger.log(TAG, VERBOSE, "Started transition", it)
- }
- }
-
- scope.launch {
keyguardInteractor.dozeTransitionModel.collect {
logger.log(TAG, VERBOSE, "Doze transition", it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt
deleted file mode 100644
index 5900a24..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt
+++ /dev/null
@@ -1,128 +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.keyguard.ui.binder
-
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewPropertyAnimator
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.launch
-
-object KeyguardAmbientIndicationAreaViewBinder {
- /**
- * Defines interface for an object that acts as the binding between the view and its view-model.
- *
- * Users of the [KeyguardBottomAreaViewBinder] class should use this to control the binder after
- * it is bound.
- */
- interface Binding {
- /**
- * Returns a collection of [ViewPropertyAnimator] instances that can be used to animate the
- * indication areas.
- */
- fun getIndicationAreaAnimators(): List<ViewPropertyAnimator>
-
- /** Notifies that device configuration has changed. */
- fun onConfigurationChanged()
-
- /** Destroys this binding, releases resources, and cancels any coroutines. */
- fun destroy()
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- fun bind(
- view: ViewGroup,
- viewModel: KeyguardAmbientIndicationViewModel,
- keyguardRootViewModel: KeyguardRootViewModel,
- ): Binding {
- val ambientIndicationArea: View? = view.findViewById(R.id.ambient_indication_container)
- val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
-
- val disposableHandle =
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- keyguardRootViewModel.alpha.collect { alpha ->
- ambientIndicationArea?.apply {
- this.importantForAccessibility =
- if (alpha == 0f) {
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- } else {
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
- }
- this.alpha = alpha
- }
- }
- }
-
- launch {
- viewModel.indicationAreaTranslationX.collect { translationX ->
- ambientIndicationArea?.translationX = translationX
- }
- }
-
- launch {
- configurationBasedDimensions
- .map { it.defaultBurnInPreventionYOffsetPx }
- .flatMapLatest { defaultBurnInOffsetY ->
- viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
- }
- .collect { translationY ->
- ambientIndicationArea?.translationY = translationY
- }
- }
-
- }
- }
-
-
- return object : Binding {
- override fun getIndicationAreaAnimators(): List<ViewPropertyAnimator> {
- return listOf(ambientIndicationArea).mapNotNull { it?.animate() }
- }
-
- override fun onConfigurationChanged() {
- configurationBasedDimensions.value = loadFromResources(view)
- }
-
- override fun destroy() {
- disposableHandle.dispose()
- }
- }
- }
-
- private fun loadFromResources(view: View): ConfigurationBasedDimensions {
- return ConfigurationBasedDimensions(
- defaultBurnInPreventionYOffsetPx =
- view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset),
- )
- }
-
- private data class ConfigurationBasedDimensions(
- val defaultBurnInPreventionYOffsetPx: Int,
- )
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 2e64c41..0cf891c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -20,10 +20,10 @@
import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.items.ClockSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
@@ -31,8 +31,13 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule.Companion.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import java.util.Optional
import javax.inject.Inject
+import javax.inject.Named
+import kotlin.jvm.optionals.getOrNull
/**
* Positions elements of the lockscreen to the default position.
@@ -47,7 +52,8 @@
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
defaultShortcutsSection: DefaultShortcutsSection,
- defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ @Named(KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ defaultAmbientIndicationAreaSection: Optional<KeyguardSection>,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
defaultStatusViewSection: DefaultStatusViewSection,
defaultStatusBarSection: DefaultStatusBarSection,
@@ -61,11 +67,11 @@
override val id: String = DEFAULT
override val sections =
- listOf(
+ listOfNotNull(
defaultIndicationAreaSection,
defaultDeviceEntryIconSection,
defaultShortcutsSection,
- defaultAmbientIndicationAreaSection,
+ defaultAmbientIndicationAreaSection.getOrNull(),
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
defaultStatusBarSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index d8b368b..14e8f89 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -19,18 +19,22 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import com.android.systemui.util.kotlin.getOrNull
+import java.util.Optional
import javax.inject.Inject
+import javax.inject.Named
/** Vertically aligns the shortcuts with the udfps. */
@SysUISingleton
@@ -39,7 +43,8 @@
constructor(
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
- defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ @Named(KeyguardSectionsModule.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ defaultAmbientIndicationAreaSection: Optional<KeyguardSection>,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
defaultStatusViewSection: DefaultStatusViewSection,
@@ -52,10 +57,10 @@
override val id: String = SHORTCUTS_BESIDE_UDFPS
override val sections =
- listOf(
+ listOfNotNull(
defaultIndicationAreaSection,
defaultDeviceEntryIconSection,
- defaultAmbientIndicationAreaSection,
+ defaultAmbientIndicationAreaSection.getOrNull(),
defaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection,
defaultStatusViewSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index 35679b8..0d397bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -20,18 +20,22 @@
import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
+import com.android.systemui.util.kotlin.getOrNull
+import java.util.Optional
import javax.inject.Inject
+import javax.inject.Named
/**
* Split-shade layout, mostly used for larger devices like foldables and tablets when in landscape
@@ -45,7 +49,8 @@
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
defaultShortcutsSection: DefaultShortcutsSection,
- defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ @Named(KeyguardSectionsModule.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ defaultAmbientIndicationAreaSection: Optional<KeyguardSection>,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
defaultStatusViewSection: DefaultStatusViewSection,
defaultStatusBarSection: DefaultStatusBarSection,
@@ -58,11 +63,11 @@
override val id: String = ID
override val sections =
- listOf(
+ listOfNotNull(
defaultIndicationAreaSection,
defaultDeviceEntryIconSection,
defaultShortcutsSection,
- defaultAmbientIndicationAreaSection,
+ defaultAmbientIndicationAreaSection.getOrNull(),
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
defaultStatusBarSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index eb01d4f6..b7a165c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -28,6 +28,8 @@
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -46,6 +48,7 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
+ private val featureFlags: FeatureFlagsClassic,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
if (keyguardBottomAreaRefactor()) {
@@ -83,20 +86,26 @@
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
+ val lockIconViewId = if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
+
constraintSet.apply {
constrainWidth(R.id.start_button, width)
constrainHeight(R.id.start_button, height)
connect(R.id.start_button, LEFT, PARENT_ID, LEFT)
- connect(R.id.start_button, RIGHT, R.id.lock_icon_view, LEFT)
- connect(R.id.start_button, TOP, R.id.lock_icon_view, TOP)
- connect(R.id.start_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
+ connect(R.id.start_button, RIGHT, lockIconViewId, LEFT)
+ connect(R.id.start_button, TOP, lockIconViewId, TOP)
+ connect(R.id.start_button, BOTTOM, lockIconViewId, BOTTOM)
constrainWidth(R.id.end_button, width)
constrainHeight(R.id.end_button, height)
connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT)
- connect(R.id.end_button, LEFT, R.id.lock_icon_view, RIGHT)
- connect(R.id.end_button, TOP, R.id.lock_icon_view, TOP)
- connect(R.id.end_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
+ connect(R.id.end_button, LEFT, lockIconViewId, RIGHT)
+ connect(R.id.end_button, TOP, lockIconViewId, TOP)
+ connect(R.id.end_button, BOTTOM, lockIconViewId, BOTTOM)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
deleted file mode 100644
index 20cb9b0..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ /dev/null
@@ -1,101 +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.keyguard.ui.view.layout.sections
-
-import android.view.LayoutInflater
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import javax.inject.Inject
-
-class DefaultAmbientIndicationAreaSection
-@Inject
-constructor(
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
- private val keyguardRootViewModel: KeyguardRootViewModel,
-) : KeyguardSection() {
- private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
-
- override fun addViews(constraintLayout: ConstraintLayout) {
- if (keyguardBottomAreaRefactor()) {
- val view =
- LayoutInflater.from(constraintLayout.context)
- .inflate(R.layout.ambient_indication, constraintLayout, false)
-
- constraintLayout.addView(view)
- }
- }
-
- override fun bindData(constraintLayout: ConstraintLayout) {
- if (keyguardBottomAreaRefactor()) {
- ambientIndicationAreaHandle =
- KeyguardAmbientIndicationAreaViewBinder.bind(
- constraintLayout,
- keyguardAmbientIndicationViewModel,
- keyguardRootViewModel,
- )
- }
- }
-
- override fun applyConstraints(constraintSet: ConstraintSet) {
- constraintSet.apply {
- constrainWidth(R.id.ambient_indication_container, MATCH_PARENT)
-
- if (keyguardUpdateMonitor.isUdfpsSupported) {
- // constrain below udfps and above indication area
- constrainHeight(R.id.ambient_indication_container, MATCH_CONSTRAINT)
- connect(R.id.ambient_indication_container, TOP, R.id.lock_icon_view, BOTTOM)
- connect(
- R.id.ambient_indication_container,
- BOTTOM,
- R.id.keyguard_indication_area,
- TOP
- )
- connect(R.id.ambient_indication_container, START, PARENT_ID, START)
- connect(R.id.ambient_indication_container, END, PARENT_ID, END)
- } else {
- // constrain above lock icon
- constrainHeight(R.id.ambient_indication_container, WRAP_CONTENT)
- connect(R.id.ambient_indication_container, BOTTOM, R.id.lock_icon_view, TOP)
- connect(R.id.ambient_indication_container, START, PARENT_ID, START)
- connect(R.id.ambient_indication_container, END, PARENT_ID, END)
- }
- }
- }
-
- override fun removeViews(constraintLayout: ConstraintLayout) {
- ambientIndicationAreaHandle?.destroy()
-
- constraintLayout.removeView(R.id.ambient_indication_container)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSectionsModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSectionsModule.kt
new file mode 100644
index 0000000..37c00b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSectionsModule.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.ui.view.layout.sections
+
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import dagger.BindsOptionalOf
+import dagger.Module
+import javax.inject.Named
+
+@Module
+abstract class KeyguardSectionsModule {
+
+ companion object {
+ const val KEYGUARD_AMBIENT_INDICATION_AREA_SECTION =
+ "keyguard_ambient_indication_area_section"
+ }
+
+ @BindsOptionalOf
+ @Named(KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ abstract fun defaultAmbientIndicationAreaSection(): KeyguardSection
+
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardAmbientIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardAmbientIndicationViewModel.kt
deleted file mode 100644
index dd3967a..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardAmbientIndicationViewModel.kt
+++ /dev/null
@@ -1,51 +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.keyguard.ui.viewmodel
-
-import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-import javax.inject.Inject
-
-class KeyguardAmbientIndicationViewModel
-@Inject
-constructor(
- private val keyguardInteractor: KeyguardInteractor,
- private val burnInHelperWrapper: BurnInHelperWrapper,
-) {
-
- /** An observable for the x-offset by which the indication area should be translated. */
- val indicationAreaTranslationX: Flow<Float> =
- keyguardInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
-
- /** Returns an observable for the y-offset by which the indication area should be translated. */
- fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
- return keyguardInteractor.dozeAmount
- .map { dozeAmount ->
- dozeAmount *
- (burnInHelperWrapper.burnInOffset(
- /* amplitude = */ defaultBurnInOffset * 2,
- /* xAxis= */ false,
- ) - defaultBurnInOffset)
- }
- .distinctUntilChanged()
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
index 8453af1..0f54e93 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
@@ -80,12 +80,13 @@
R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
}
+ val singleAppOptionDisabled =
+ appName != null &&
+ mediaProjectionConfig?.regionToCapture ==
+ MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
+
val singleAppDisabledText =
- if (
- appName != null &&
- mediaProjectionConfig?.regionToCapture ==
- MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
- ) {
+ if (singleAppOptionDisabled) {
context.getString(
R.string.media_projection_entry_app_permission_dialog_single_app_disabled,
appName
@@ -93,19 +94,26 @@
} else {
null
}
- return listOf(
- ScreenShareOption(
- mode = SINGLE_APP,
- spinnerText = R.string.screen_share_permission_dialog_option_single_app,
- warningText = singleAppWarningText,
- spinnerDisabledText = singleAppDisabledText,
- ),
- ScreenShareOption(
- mode = ENTIRE_SCREEN,
- spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
- warningText = entireScreenWarningText
+ val options =
+ listOf(
+ ScreenShareOption(
+ mode = SINGLE_APP,
+ spinnerText = R.string.screen_share_permission_dialog_option_single_app,
+ warningText = singleAppWarningText,
+ spinnerDisabledText = singleAppDisabledText,
+ ),
+ ScreenShareOption(
+ mode = ENTIRE_SCREEN,
+ spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
+ warningText = entireScreenWarningText
+ )
)
- )
+ return if (singleAppOptionDisabled) {
+ // Make sure "Entire screen" is the first option when "Single App" is disabled.
+ options.reversed()
+ } else {
+ options
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index ead10d6..9f4ea27 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -181,12 +181,16 @@
if (mCancelTimeoutRunnable != null) {
mCancelTimeoutRunnable.run();
}
- finish();
+ requestFinish();
}
return super.onKeyDown(keyCode, event);
}
+ protected void requestFinish() {
+ finish();
+ }
+
private boolean triggeredByBrightnessKey() {
return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false);
}
@@ -197,6 +201,6 @@
}
final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
AccessibilityManager.FLAG_CONTENT_CONTROLS);
- mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout);
+ mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::requestFinish, timeout);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 5a007fc..7201b35 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -29,7 +29,6 @@
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -41,7 +40,6 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-import static com.android.systemui.statusbar.VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.util.DumpUtilsKt.asIndenting;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -64,7 +62,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.Process;
import android.os.Trace;
import android.os.UserManager;
import android.os.VibrationEffect;
@@ -2841,16 +2838,7 @@
}
if (!mStatusBarStateController.isDozing()) {
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mVibratorHelper.performHapticFeedback(mView, HapticFeedbackConstants.REJECT);
- } else {
- mVibratorHelper.vibrate(
- Process.myUid(),
- mView.getContext().getPackageName(),
- ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT,
- "falsing-additional-tap-required",
- TOUCH_VIBRATION_ATTRIBUTES);
- }
+ mVibratorHelper.performHapticFeedback(mView, HapticFeedbackConstants.REJECT);
}
}
@@ -3678,14 +3666,10 @@
private void maybeVibrateOnOpening(boolean openingWithTouch) {
if (mVibrateOnOpening && mBarState != KEYGUARD && mBarState != SHADE_LOCKED) {
if (!openingWithTouch || !mHasVibratedOnOpen) {
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mVibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_START
- );
- } else {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
- }
+ mVibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_START
+ );
mHasVibratedOnOpen = true;
mShadeLog.v("Vibrating on opening, mHasVibratedOnOpen=true");
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/OWNERS b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
index c8b6a2e..bbcf10b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
@@ -6,8 +6,12 @@
per-file NotificationsQuickSettingsContainer.java = kozynski@google.com, asc@google.com
per-file NotificationsQSContainerController.kt = kozynski@google.com, asc@google.com
-per-file *ShadeHeader* = kozynski@google.com, asc@google.com
-per-file *Shade* = justinweir@google.com
+per-file *ShadeHeader* = syeonlee@google.com, kozynski@google.com, asc@google.com
+
+per-file *Interactor* = set noparent
+per-file *Interactor* = justinweir@google.com, syeonlee@google.com, nijamkin@google.com
+per-file *Repository* = set noparent
+per-file *Repository* = justinweir@google.com, syeonlee@google.com, nijamkin@google.com
per-file NotificationShadeWindowViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com
per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com, juliacr@google.com
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 14ec08f35..46488c6 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
@@ -4668,22 +4668,6 @@
return mClearAllInProgress;
}
- public boolean isFooterViewNotGone() {
- return mFooterView != null
- && mFooterView.getVisibility() != View.GONE
- && !mFooterView.willBeGone();
- }
-
- public boolean isFooterViewContentVisible() {
- return mFooterView != null && mFooterView.isContentVisible();
- }
-
- public int getFooterViewHeightWithPadding() {
- return mFooterView == null ? 0 : mFooterView.getHeight()
- + mPaddingBetweenElements
- + mGapHeight;
- }
-
/**
* @return the padding after the media header on the lockscreen
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 2cf0c26..3e140a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -858,10 +858,6 @@
return row.getVisibility() == View.VISIBLE;
}
- public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
- return mNotificationRoundnessManager.isViewAffectedBySwipe(expandableView);
- }
-
public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mView.addOnExpandedHeightChangedListener(listener);
}
@@ -1261,18 +1257,6 @@
mView.setPanelFlinging(flinging);
}
- public boolean isFooterViewNotGone() {
- return mView.isFooterViewNotGone();
- }
-
- public boolean isFooterViewContentVisible() {
- return mView.isFooterViewContentVisible();
- }
-
- public int getFooterViewHeightWithPadding() {
- return mView.getFooterViewHeightWithPadding();
- }
-
/**
* Sets whether the bouncer is currently showing. Should only be called from
* {@link CentralSurfaces}.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 818ba95..3304b98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepository;
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepositoryImpl;
import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepositoryModule;
+import com.android.systemui.statusbar.policy.data.repository.ZenModeRepositoryModule;
import dagger.Binds;
import dagger.Module;
@@ -81,7 +82,7 @@
import javax.inject.Named;
/** Dagger Module for code in the statusbar.policy package. */
-@Module(includes = { DeviceProvisioningRepositoryModule.class })
+@Module(includes = { DeviceProvisioningRepositoryModule.class, ZenModeRepositoryModule.class })
public interface StatusBarPolicyModule {
String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt
new file mode 100644
index 0000000..94ab58a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt
@@ -0,0 +1,77 @@
+/*
+ * 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 android.app.NotificationManager
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.policy.ZenModeController
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * A repository that holds information about the status and configuration of Zen Mode (or Do Not
+ * Disturb/DND Mode).
+ */
+interface ZenModeRepository {
+ val zenMode: Flow<Int>
+ val consolidatedNotificationPolicy: Flow<NotificationManager.Policy?>
+}
+
+class ZenModeRepositoryImpl
+@Inject
+constructor(
+ private val zenModeController: ZenModeController,
+) : ZenModeRepository {
+ // TODO(b/308591859): ZenModeController should use flows instead of callbacks. The
+ // conflatedCallbackFlows here should be replaced eventually, see:
+ // https://docs.google.com/document/d/1gAiuYupwUAFdbxkDXa29A4aFNu7XoCd7sCIk31WTnHU/edit?resourcekey=0-J4ZBiUhLhhQnNobAcI2vIw
+
+ override val zenMode: Flow<Int> = conflatedCallbackFlow {
+ val callback =
+ object : ZenModeController.Callback {
+ override fun onZenChanged(zen: Int) {
+ trySend(zen)
+ }
+ }
+ zenModeController.addCallback(callback)
+ trySend(zenModeController.zen)
+
+ awaitClose { zenModeController.removeCallback(callback) }
+ }
+
+ override val consolidatedNotificationPolicy: Flow<NotificationManager.Policy?> =
+ conflatedCallbackFlow {
+ val callback =
+ object : ZenModeController.Callback {
+ override fun onConsolidatedPolicyChanged(policy: NotificationManager.Policy?) {
+ trySend(policy)
+ }
+ }
+ zenModeController.addCallback(callback)
+ trySend(zenModeController.consolidatedPolicy)
+
+ awaitClose { zenModeController.removeCallback(callback) }
+ }
+}
+
+@Module
+interface ZenModeRepositoryModule {
+ @Binds fun bindImpl(impl: ZenModeRepositoryImpl): ZenModeRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
new file mode 100644
index 0000000..ae31851
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.domain.interactor
+
+import android.provider.Settings
+import com.android.systemui.statusbar.policy.data.repository.ZenModeRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/**
+ * An interactor that performs business logic related to the status and configuration of Zen Mode
+ * (or Do Not Disturb/DND Mode).
+ */
+class ZenModeInteractor @Inject constructor(repository: ZenModeRepository) {
+ val isZenModeEnabled: Flow<Boolean> =
+ repository.zenMode
+ .map {
+ when (it) {
+ Settings.Global.ZEN_MODE_ALARMS -> true
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS -> true
+ Settings.Global.ZEN_MODE_NO_INTERRUPTIONS -> true
+ Settings.Global.ZEN_MODE_OFF -> false
+ else -> false
+ }
+ }
+ .distinctUntilChanged()
+
+ val areNotificationsHiddenInShade: Flow<Boolean> =
+ combine(isZenModeEnabled, repository.consolidatedNotificationPolicy) { dndEnabled, policy ->
+ if (!dndEnabled) {
+ false
+ } else {
+ val showInNotificationList = policy?.showInNotificationList() ?: true
+ !showInNotificationList
+ }
+ }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index fc414b6..2f2c4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -39,7 +39,6 @@
import com.android.app.animation.Interpolators
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
-import com.android.systemui.res.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text.Companion.loadText
@@ -48,9 +47,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -96,7 +94,6 @@
wakeLockBuilder: WakeLock.Builder,
systemClock: SystemClock,
tempViewUiEventLogger: TemporaryViewUiEventLogger,
- private val featureFlags: FeatureFlags,
) :
TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
@@ -234,18 +231,14 @@
maybeGetAccessibilityFocus(newInfo, currentView)
// ---- Haptics ----
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(parent, newInfo.vibrationConstant)
- } else {
- newInfo.vibrationEffect?.let {
- vibratorHelper.vibrate(
- Process.myUid(),
- context.getApplicationContext().getPackageName(),
- it,
- newInfo.windowTitle,
- VIBRATION_ATTRIBUTES,
- )
- }
+ newInfo.vibrationEffect?.let {
+ vibratorHelper.vibrate(
+ Process.myUid(),
+ context.getApplicationContext().getPackageName(),
+ it,
+ newInfo.windowTitle,
+ VIBRATION_ATTRIBUTES,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 4449f8d0..7475388 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -17,13 +17,12 @@
package com.android.systemui.temporarydisplay.chipbar
import android.os.VibrationEffect
-import android.view.HapticFeedbackConstants
import android.view.View
import androidx.annotation.AttrRes
import com.android.internal.logging.InstanceId
-import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.res.R
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.temporarydisplay.ViewPriority
@@ -43,7 +42,6 @@
val text: Text,
val endItem: ChipbarEndItem?,
val vibrationEffect: VibrationEffect? = null,
- val vibrationConstant: Int = HapticFeedbackConstants.NO_HAPTICS,
val allowSwipeToDismiss: Boolean = false,
override val windowTitle: String,
override val wakeReason: String,
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 d2b81e0..00ea78f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -17,14 +17,14 @@
package com.android.systemui.biometrics
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import dagger.BindsInstance
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index b439fcf..722c11d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -18,9 +18,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index 7fb0dd5..3d094fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -18,9 +18,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
index 570dfb3..e9399cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -18,7 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 43d70ad..76c2589 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.keyguard.ui.view.layout.items.ClockSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
@@ -49,6 +48,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.Optional
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@@ -60,7 +60,7 @@
@Mock private lateinit var mDefaultDeviceEntryIconSection: DefaultDeviceEntryIconSection
@Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
@Mock
- private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
+ private lateinit var defaultAmbientIndicationAreaSection: Optional<KeyguardSection>
@Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
@Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection
@Mock private lateinit var defaultStatusBarViewSection: DefaultStatusBarSection
@@ -98,7 +98,7 @@
fun replaceViews() {
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(null, constraintLayout)
- underTest.sections.forEach { verify(it).addViews(constraintLayout) }
+ underTest.sections.forEach { verify(it)?.addViews(constraintLayout) }
}
@Test
@@ -110,7 +110,7 @@
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(prevBlueprint, constraintLayout)
underTest.sections.minus(mDefaultDeviceEntryIconSection).forEach {
- verify(it, never()).addViews(constraintLayout)
+ verify(it, never())?.addViews(constraintLayout)
}
verify(mDefaultDeviceEntryIconSection).addViews(constraintLayout)
@@ -121,6 +121,6 @@
fun applyConstraints() {
val cs = ConstraintSet()
underTest.applyConstraints(cs)
- underTest.sections.forEach { verify(it).applyConstraints(cs) }
+ underTest.sections.forEach { verify(it)?.applyConstraints(cs) }
}
}
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 259c74f..d3019f1 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
@@ -21,18 +21,16 @@
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
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 as AConfigFlags
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -46,6 +44,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.ClockController
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -64,7 +64,6 @@
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -109,10 +108,7 @@
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- val featureFlags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.FACE_AUTH_REFACTOR, true)
- }
+ val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
keyguardInteractor = withDeps.keyguardInteractor
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 4074851..c50be04 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
@@ -18,15 +18,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.collectLastValue
+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
@@ -39,6 +37,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
@@ -77,15 +77,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
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 5c85357..26704da 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
@@ -18,14 +18,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -35,13 +33,14 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
-import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -69,15 +68,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
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 4cbefa3d..ff3135a6 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
@@ -18,14 +18,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -35,6 +33,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
@@ -68,15 +68,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
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 4f56435..8afd8e4 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
@@ -18,13 +18,11 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -34,6 +32,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
@@ -69,15 +69,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 83145bd..3be50b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -35,15 +35,13 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -113,7 +111,6 @@
private lateinit var uiEventLogger: MediaTttSenderUiEventLogger
private lateinit var tempViewUiEventLogger: TemporaryViewUiEventLogger
private val defaultTimeout = context.resources.getInteger(R.integer.heads_up_notification_decay)
- private val featureFlags = FakeFeatureFlags()
@Before
fun setUp() {
@@ -163,9 +160,7 @@
fakeWakeLockBuilder,
fakeClock,
tempViewUiEventLogger,
- featureFlags
)
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
chipbarCoordinator.start()
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 152be65..6e487cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -25,9 +25,9 @@
import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
@@ -182,5 +182,15 @@
brightnessControllerFactory,
mainExecutor,
accessibilityMgr
- )
+ ) {
+ private var finishing = false
+
+ override fun isFinishing(): Boolean {
+ return finishing
+ }
+
+ override fun requestFinish() {
+ finishing = true
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 0f90007..f1429b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -374,7 +374,6 @@
mFeatureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false);
mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
- mFeatureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false);
mFeatureFlags.set(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT, false);
mSetFlagsRule.disableFlags(KeyguardShadeMigrationNssl.FLAG_NAME);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index 7ad84d6..4df7ef5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade
-import android.os.VibrationEffect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.HapticFeedbackConstants
@@ -26,13 +25,10 @@
import com.android.internal.util.CollectionUtils
import com.android.keyguard.KeyguardClockSwitch.LARGE
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -62,9 +58,6 @@
override fun getMainDispatcher() = Dispatchers.Main.immediate
- private val ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT =
- VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false)
-
@Test
fun testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() = runTest {
launch(Dispatchers.Main.immediate) { givenViewAttached() }
@@ -158,31 +151,8 @@
}
@Test
- fun doubleTapRequired_onKeyguard_oneWayHapticsDisabled_usesOldVibrate() = runTest {
+ fun doubleTapRequired_onKeyguard_usesPerformHapticFeedback() = runTest {
launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
- val listener = getFalsingTapListener()
- mStatusBarStateController.setState(KEYGUARD)
-
- listener.onAdditionalTapRequired()
- val packageName = mView.context.packageName
- verify(mKeyguardIndicationController).showTransientIndication(anyInt())
- verify(mVibratorHelper)
- .vibrate(
- any(),
- eq(packageName),
- eq(ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT),
- eq("falsing-additional-tap-required"),
- eq(VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES)
- )
- }
- advanceUntilIdle()
- }
-
- @Test
- fun doubleTapRequired_onKeyguard_oneWayHapticsEnabled_usesPerformHapticFeedback() = runTest {
- launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
val listener = getFalsingTapListener()
mStatusBarStateController.setState(KEYGUARD)
@@ -208,32 +178,8 @@
}
@Test
- fun doubleTapRequired_shadeLocked_oneWayHapticsDisabled_usesOldVibrate() = runTest {
+ fun doubleTapRequired_shadeLocked_usesPerformHapticFeedback() = runTest {
launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
- val listener = getFalsingTapListener()
- val packageName = mView.context.packageName
- mStatusBarStateController.setState(SHADE_LOCKED)
-
- listener.onAdditionalTapRequired()
- verify(mVibratorHelper)
- .vibrate(
- any(),
- eq(packageName),
- eq(ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT),
- eq("falsing-additional-tap-required"),
- eq(VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES)
- )
-
- verify(mTapAgainViewController).show()
- }
- advanceUntilIdle()
- }
-
- @Test
- fun doubleTapRequired_shadeLocked_oneWayHapticsEnabled_usesPerformHapticFeedback() = runTest {
- launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
val listener = getFalsingTapListener()
mStatusBarStateController.setState(SHADE_LOCKED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index ff7443f..32daccb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -22,13 +22,11 @@
import android.content.pm.UserInfo
import android.os.UserManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -45,6 +43,8 @@
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 970a0f7..fdbdc79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,8 +5,8 @@
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
+import com.android.systemui.TestMocksModule
import com.android.systemui.ExpandHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
@@ -607,9 +607,9 @@
@Component.Factory
interface Factory {
fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
): TestComponent
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
index d479937..f3094cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.statusbar.notification.data.repository
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
index 707026e..b775079 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
@@ -16,8 +16,8 @@
import android.app.StatusBarManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
index bb6f1b6..bb3113a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -14,20 +14,17 @@
package com.android.systemui.statusbar.notification.domain.interactor
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Test
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index 05deb1c..0341035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -17,14 +17,14 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.runTest
import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
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 c2c33de..68761ef 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
@@ -18,14 +18,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -39,6 +37,8 @@
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
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 87e9735..c2a1519 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
@@ -20,14 +20,12 @@
import android.graphics.drawable.Icon
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -42,6 +40,8 @@
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 7423c2d..917569c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -19,16 +19,16 @@
import android.os.PowerManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.runTest
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index db8f217..9c70c82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -19,13 +19,11 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
@@ -39,6 +37,8 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.user.domain.UserDomainLayerModule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt
new file mode 100644
index 0000000..004f679
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt
@@ -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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.policy.data.repository
+
+import android.app.NotificationManager
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+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
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ZenModeRepositoryImplTest : SysuiTestCase() {
+ @Mock lateinit var zenModeController: ZenModeController
+
+ lateinit var underTest: ZenModeRepositoryImpl
+
+ private val testPolicy = NotificationManager.Policy(0, 1, 0)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest = ZenModeRepositoryImpl(zenModeController)
+ }
+
+ @Test
+ fun zenMode_reflectsCurrentControllerState() = runTest {
+ whenever(zenModeController.zen).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ val zenMode by collectLastValue(underTest.zenMode)
+ assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ }
+
+ @Test
+ fun zenMode_updatesWhenControllerStateChanges() = runTest {
+ val zenMode by collectLastValue(underTest.zenMode)
+ runCurrent()
+ whenever(zenModeController.zen).thenReturn(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ withArgCaptor { Mockito.verify(zenModeController).addCallback(capture()) }
+ .onZenChanged(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ }
+
+ @Test
+ fun policy_reflectsCurrentControllerState() {
+ runTest {
+ whenever(zenModeController.consolidatedPolicy).thenReturn(testPolicy)
+ val policy by collectLastValue(underTest.consolidatedNotificationPolicy)
+ assertThat(policy).isEqualTo(testPolicy)
+ }
+ }
+
+ @Test
+ fun policy_updatesWhenControllerStateChanges() = runTest {
+ val policy by collectLastValue(underTest.consolidatedNotificationPolicy)
+ runCurrent()
+ whenever(zenModeController.consolidatedPolicy).thenReturn(testPolicy)
+ withArgCaptor { Mockito.verify(zenModeController).addCallback(capture()) }
+ .onConsolidatedPolicyChanged(testPolicy)
+ assertThat(policy).isEqualTo(testPolicy)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
new file mode 100644
index 0000000..78e7971
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.domain.interactor
+
+import android.app.NotificationManager.Policy
+import android.provider.Settings
+import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.collectLastValue
+import com.android.runCurrent
+import com.android.runTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import org.junit.Test
+
+@SmallTest
+class ZenModeInteractorTest : SysuiTestCase() {
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<ZenModeInteractor> {
+
+ val repository: FakeZenModeRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(@BindsInstance test: SysuiTestCase): TestComponent
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerZenModeInteractorTest_TestComponent.factory().create(test = this)
+
+ @Test
+ fun testIsZenModeEnabled_off() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_OFF
+ runCurrent()
+
+ assertThat(enabled).isFalse()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_alarms() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_ALARMS
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_importantInterruptions() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_noInterruptions() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_unknown() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = 4 // this should fail if we ever add another zen mode type
+ runCurrent()
+
+ assertThat(enabled).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_noPolicy() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.consolidatedNotificationPolicy.value = null
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_zenOffShadeSuppressed() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.consolidatedNotificationPolicy.value =
+ policyWithSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ repository.zenMode.value = Settings.Global.ZEN_MODE_OFF
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_zenOnShadeNotSuppressed() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.consolidatedNotificationPolicy.value =
+ policyWithSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_STATUS_BAR)
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_zenOnShadeSuppressed() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.consolidatedNotificationPolicy.value =
+ policyWithSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(hidden).isTrue()
+ }
+}
+
+fun policyWithSuppressedVisualEffects(suppressedVisualEffects: Int) =
+ Policy(
+ /* priorityCategories = */ 0,
+ /* priorityCallSenders = */ 0,
+ /* priorityMessageSenders = */ 0,
+ /* suppressedVisualEffects = */ suppressedVisualEffects
+ )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 1e7e184..1466d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -21,7 +21,6 @@
import android.os.VibrationEffect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -33,7 +32,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription
@@ -42,9 +40,8 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewUiEvent
@@ -94,7 +91,6 @@
private lateinit var fakeExecutor: FakeExecutor
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var uiEventLogger: TemporaryViewUiEventLogger
- private val featureFlags = FakeFeatureFlags()
@Before
fun setUp() {
@@ -131,10 +127,8 @@
fakeWakeLockBuilder,
fakeClock,
uiEventLogger,
- featureFlags
)
underTest.start()
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
}
@Test
@@ -494,23 +488,6 @@
)
}
- @Test
- fun displayView_oneWayHapticsEnabled_usesPerformHapticFeedback() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
- val constant: Int = HapticFeedbackConstants.CONFIRM
- underTest.displayView(
- createChipbarInfo(
- Icon.Resource(R.id.check_box, null),
- Text.Loaded("text"),
- endItem = null,
- vibrationEffect = null,
- vibrationConstant = constant
- )
- )
-
- verify(vibratorHelper).performHapticFeedback(any(), eq(constant))
- }
-
/** Regression test for b/266119467. */
@Test
fun displayView_animationFailure_viewsStillBecomeVisible() {
@@ -729,14 +706,12 @@
endItem: ChipbarEndItem?,
vibrationEffect: VibrationEffect? = null,
allowSwipeToDismiss: Boolean = false,
- vibrationConstant: Int = HapticFeedbackConstants.NO_HAPTICS,
): ChipbarInfo {
return ChipbarInfo(
TintedIcon(startIcon, tint = null),
text,
endItem,
vibrationEffect,
- vibrationConstant,
allowSwipeToDismiss,
windowTitle = WINDOW_TITLE,
wakeReason = WAKE_REASON,
diff --git a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
index 360aa0f..de310b4 100644
--- a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/SysUITestModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
index 97e43ad..f2c32e3d 100644
--- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import android.content.Context
import android.content.res.Resources
import android.testing.TestableContext
import android.testing.TestableResources
-import com.android.systemui.FakeSystemUiModule
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/TestMocksModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index fd50f15..37a4f61 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
@@ -24,7 +24,6 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
index 5aece1b..16dab40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
@@ -16,7 +16,14 @@
package com.android.systemui.statusbar.policy.data
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepositoryModule
+import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepositoryModule
import dagger.Module
-@Module(includes = [FakeDeviceProvisioningRepositoryModule::class])
+@Module(
+ includes =
+ [
+ FakeDeviceProvisioningRepositoryModule::class,
+ FakeZenModeRepositoryModule::class,
+ ]
+)
object FakeStatusBarPolicyDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt
new file mode 100644
index 0000000..4059930
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt
@@ -0,0 +1,43 @@
+/*
+ * 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 android.app.NotificationManager
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class FakeZenModeRepository @Inject constructor() : ZenModeRepository {
+ override val zenMode: MutableStateFlow<Int> = MutableStateFlow(Settings.Global.ZEN_MODE_OFF)
+ override val consolidatedNotificationPolicy: MutableStateFlow<NotificationManager.Policy?> =
+ MutableStateFlow(
+ NotificationManager.Policy(
+ /* priorityCategories = */ 0,
+ /* priorityCallSenders = */ 0,
+ /* priorityMessageSenders = */ 0,
+ )
+ )
+}
+
+@Module
+interface FakeZenModeRepositoryModule {
+ @Binds fun bindFake(fake: FakeZenModeRepository): ZenModeRepository
+}
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 4c224fb..fb521e1 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -359,7 +359,7 @@
}
@Test
- public void testUpdateWallpaperComponent_doesApplyLater() throws IOException {
+ public void testUpdateWallpaperComponent_systemAndLock() throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
/* which */ FLAG_LOCK | FLAG_SYSTEM);
@@ -377,7 +377,7 @@
}
@Test
- public void testUpdateWallpaperComponent_applyToLockFalse_doesApplyLaterOnlyToMainScreen()
+ public void testUpdateWallpaperComponent_systemOnly()
throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
@@ -617,7 +617,7 @@
mWallpaperBackupAgent.onRestoreFinished();
- // wallpaper will be applied to home & lock screen, a success for both screens in expected
+ // wallpaper will be applied to home & lock screen, a success for both screens is expected
DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
assertThat(result).isNotNull();
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index fb8db21..550e17b 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -21,7 +21,6 @@
defaults: ["platform_service_defaults"],
srcs: [
":services.companion-sources",
- ":VirtualCamera-aidl-sources",
],
libs: [
"app-compat-annotations",
@@ -30,13 +29,6 @@
static_libs: [
"ukey2_jni",
"virtualdevice_flags_lib",
+ "virtual_camera_service_aidl-java",
],
}
-
-filegroup {
- name: "VirtualCamera-aidl-sources",
- srcs: [
- "java/com/android/server/companion/virtual/camera/*.aidl",
- ],
- path: "java",
-}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 562fe3b..118943d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -51,7 +51,7 @@
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
-import android.companion.virtual.camera.IVirtualCamera;
+import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
@@ -277,7 +277,7 @@
runningAppsChangedCallback,
params,
DisplayManagerGlobal.getInstance(),
- Flags.virtualCamera() ? new VirtualCameraController(context) : null);
+ Flags.virtualCamera() ? new VirtualCameraController() : null);
}
@VisibleForTesting
@@ -692,7 +692,8 @@
final long ident = Binder.clearCallingIdentity();
try {
mInputController.createDpad(config.getInputDeviceName(), config.getVendorId(),
- config.getProductId(), deviceToken, config.getAssociatedDisplayId());
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -710,7 +711,8 @@
final long ident = Binder.clearCallingIdentity();
try {
mInputController.createKeyboard(config.getInputDeviceName(), config.getVendorId(),
- config.getProductId(), deviceToken, config.getAssociatedDisplayId(),
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()),
config.getLanguageTag(), config.getLayoutType());
} finally {
Binder.restoreCallingIdentity(ident);
@@ -776,7 +778,8 @@
try {
mInputController.createNavigationTouchpad(
config.getInputDeviceName(), config.getVendorId(),
- config.getProductId(), deviceToken, config.getAssociatedDisplayId(),
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()),
touchpadHeight, touchpadWidth);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -954,13 +957,28 @@
}
}
+ @Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
- public void registerVirtualCamera(@NonNull IVirtualCamera camera) {
+ public void registerVirtualCamera(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
super.registerVirtualCamera_enforcePermission();
+ Objects.requireNonNull(cameraConfig);
if (mVirtualCameraController == null) {
- return;
+ throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.registerCamera(Objects.requireNonNull(camera));
+ mVirtualCameraController.registerCamera(Objects.requireNonNull(cameraConfig));
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void unregisterVirtualCamera(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
+ super.unregisterVirtualCamera_enforcePermission();
+ Objects.requireNonNull(cameraConfig);
+ if (mVirtualCameraController == null) {
+ throw new UnsupportedOperationException("Virtual camera controller is not available");
+ }
+ mVirtualCameraController.unregisterCamera(Objects.requireNonNull(cameraConfig));
}
@Override
@@ -987,6 +1005,20 @@
}
}
+ // For display mirroring, we want to dispatch all key events to the source (default) display,
+ // as the virtual display doesn't have any focused windows. Hence, call this for
+ // associating any input device to the source display if the input device emits any key events.
+ private int getTargetDisplayIdForInput(int displayId) {
+ if (!Flags.interactiveScreenMirror()) {
+ return displayId;
+ }
+
+ DisplayManagerInternal displayManager = LocalServices.getService(
+ DisplayManagerInternal.class);
+ int mirroredDisplayId = displayManager.getDisplayIdToMirror(displayId);
+ return mirroredDisplayId == Display.INVALID_DISPLAY ? displayId : mirroredDisplayId;
+ }
+
@GuardedBy("mVirtualDeviceLock")
private GenericWindowPolicyController createWindowPolicyControllerLocked(
@NonNull Set<String> displayCategories) {
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl b/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl
deleted file mode 100644
index a4c1c42..0000000
--- a/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl
+++ /dev/null
@@ -1,39 +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.server.companion.virtual.camera;
-
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-
-/**
- * AIDL Interface to communicate with the VirtualCamera HAL
- * @hide
- */
-interface IVirtualCameraService {
-
- /**
- * Registers a new camera with the virtual camera hal.
- * @return true if the camera was successfully registered
- */
- boolean registerCamera(in IVirtualCamera camera);
-
- /**
- * Unregisters the camera from the virtual camera hal. After this call the virtual camera won't
- * be visible to the camera framework anymore.
- */
- void unregisterCamera(in IVirtualCamera camera);
-}
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 031d949..06be3f3 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -16,207 +16,127 @@
package com.android.server.companion.virtual.camera;
+import static com.android.server.companion.virtual.camera.VirtualCameraConversionUtil.getServiceCameraConfiguration;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
+import android.companion.virtual.camera.VirtualCameraConfig;
+import android.companion.virtualcamera.IVirtualCameraService;
+import android.companion.virtualcamera.VirtualCameraConfiguration;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
+import android.os.ServiceManager;
+import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Set;
/**
* Manages the registration and removal of virtual camera from the server side.
*
* <p>This classes delegate calls to the virtual camera service, so it is dependent on the service
- * to be up and running
+ * to be up and running.
*/
-public class VirtualCameraController implements IBinder.DeathRecipient, ServiceConnection {
+public final class VirtualCameraController implements IBinder.DeathRecipient {
- private static class VirtualCameraInfo {
-
- private final IVirtualCamera mVirtualCamera;
- private boolean mIsRegistered;
-
- VirtualCameraInfo(IVirtualCamera virtualCamera) {
- mVirtualCamera = virtualCamera;
- }
- }
-
+ private static final String VIRTUAL_CAMERA_SERVICE_NAME = "virtual_camera";
private static final String TAG = "VirtualCameraController";
- private static final String VIRTUAL_CAMERA_SERVICE_PACKAGE = "com.android.virtualcamera";
- private static final String VIRTUAL_CAMERA_SERVICE_CLASS = ".VirtualCameraService";
- private final Context mContext;
-
- @Nullable private IVirtualCameraService mVirtualCameraService = null;
+ @Nullable private IVirtualCameraService mVirtualCameraService;
@GuardedBy("mCameras")
- private final Map<IVirtualCamera, VirtualCameraInfo> mCameras = new HashMap<>(1);
+ private final Set<VirtualCameraConfig> mCameras = new ArraySet<>();
- public VirtualCameraController(Context context) {
- mContext = context;
+ public VirtualCameraController() {
connectVirtualCameraService();
}
- private void connectVirtualCameraService() {
- final long callingId = Binder.clearCallingIdentity();
+ @VisibleForTesting
+ VirtualCameraController(IVirtualCameraService virtualCameraService) {
+ mVirtualCameraService = virtualCameraService;
+ }
+
+ /**
+ * Register a new virtual camera with the given config.
+ *
+ * @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
+ */
+ public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) {
+ // Try to connect to service if not connected already.
+ if (mVirtualCameraService == null) {
+ connectVirtualCameraService();
+ }
+ // Throw exception if we are unable to connect to service.
+ if (mVirtualCameraService == null) {
+ throw new IllegalStateException("Virtual camera service is not connected.");
+ }
+
try {
- Intent intent = new Intent();
- intent.setPackage(VIRTUAL_CAMERA_SERVICE_PACKAGE);
- intent.setComponent(
- ComponentName.createRelative(
- VIRTUAL_CAMERA_SERVICE_PACKAGE, VIRTUAL_CAMERA_SERVICE_CLASS));
- mContext.startServiceAsUser(intent, UserHandle.SYSTEM);
- if (!mContext.bindServiceAsUser(
- intent,
- this,
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.SYSTEM)) {
- mContext.unbindService(this);
- Log.w(
- TAG,
- "connectVirtualCameraService: Failed to connect to the virtual camera "
- + "service");
- }
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
- private void forwardPendingRegistrations() {
- IVirtualCameraService cameraService = mVirtualCameraService;
- if (cameraService == null) {
- return;
- }
- synchronized (mCameras) {
- for (VirtualCameraInfo cameraInfo : mCameras.values()) {
- if (cameraInfo.mIsRegistered) {
- continue;
- }
- try {
- cameraService.registerCamera(cameraInfo.mVirtualCamera);
- cameraInfo.mIsRegistered = true;
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
- }
- }
-
- /**
- * Remove the virtual camera with the provided name
- *
- * @param camera The name of the camera to remove
- */
- public void unregisterCamera(@NonNull IVirtualCamera camera) {
- IVirtualCameraService virtualCameraService = mVirtualCameraService;
- if (virtualCameraService != null) {
- try {
- virtualCameraService.unregisterCamera(camera);
+ if (registerCameraWithService(cameraConfig)) {
synchronized (mCameras) {
- VirtualCameraInfo cameraInfo = mCameras.remove(camera);
- cameraInfo.mIsRegistered = false;
+ mCameras.add(cameraConfig);
}
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ } else {
+ // TODO(b/310857519): Revisit this to find a better way of indicating failure.
+ throw new RuntimeException("Failed to register virtual camera.");
}
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
/**
- * Register a new virtual camera with the provided characteristics.
+ * Unregister the virtual camera with the given config.
*
- * @param camera The {@link IVirtualCamera} producing the image to communicate with the client.
- * @throws IllegalArgumentException if the characteristics could not be parsed.
+ * @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
- public void registerCamera(@NonNull IVirtualCamera camera) {
- IVirtualCameraService service = mVirtualCameraService;
- VirtualCameraInfo virtualCameraInfo = new VirtualCameraInfo(camera);
- synchronized (mCameras) {
- mCameras.put(camera, virtualCameraInfo);
- }
- if (service != null) {
- try {
- if (service.registerCamera(camera)) {
- virtualCameraInfo.mIsRegistered = true;
- return;
- }
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ public void unregisterCamera(@NonNull VirtualCameraConfig cameraConfig) {
+ try {
+ if (mVirtualCameraService == null) {
+ Slog.w(TAG, "Virtual camera service is not connected.");
+ } else {
+ mVirtualCameraService.unregisterCamera(cameraConfig.getCallback().asBinder());
}
+ synchronized (mCameras) {
+ mCameras.remove(cameraConfig);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
-
- // Service was not available or registration failed, save the registration for later
- connectVirtualCameraService();
}
@Override
public void binderDied() {
- Log.d(TAG, "binderDied");
+ Slog.d(TAG, "Virtual camera service died.");
mVirtualCameraService = null;
- }
-
- @Override
- public void onBindingDied(ComponentName name) {
- mVirtualCameraService = null;
- Log.d(TAG, "onBindingDied() called with: name = [" + name + "]");
- }
-
- @Override
- public void onNullBinding(ComponentName name) {
- mVirtualCameraService = null;
- Log.d(TAG, "onNullBinding() called with: name = [" + name + "]");
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "onServiceConnected: " + name.toString());
- mVirtualCameraService = IVirtualCameraService.Stub.asInterface(service);
- try {
- service.linkToDeath(this, 0);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ synchronized (mCameras) {
+ mCameras.clear();
}
- forwardPendingRegistrations();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "onServiceDisconnected() called with: name = [" + name + "]");
- mVirtualCameraService = null;
}
/** Release resources associated with this controller. */
public void close() {
- if (mVirtualCameraService == null) {
- return;
- }
synchronized (mCameras) {
- mCameras.forEach(
- (name, cameraInfo) -> {
- try {
- mVirtualCameraService.unregisterCamera(name);
- } catch (RemoteException e) {
- Log.w(
- TAG,
- "close(): Camera failed to be removed on camera service.",
- e);
- }
- });
+ if (mVirtualCameraService == null) {
+ Slog.w(TAG, "Virtual camera service is not connected.");
+ } else {
+ for (VirtualCameraConfig config : mCameras) {
+ try {
+ mVirtualCameraService.unregisterCamera(config.getCallback().asBinder());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "close(): Camera failed to be removed on camera "
+ + "service.", e);
+ }
+ }
+ }
+ mCameras.clear();
}
- mContext.unbindService(this);
+ mVirtualCameraService = null;
}
/** Dumps information about this {@link VirtualCameraController} for debugging purposes. */
@@ -226,20 +146,34 @@
fout.printf("%sService:%s\n", indent, mVirtualCameraService);
synchronized (mCameras) {
fout.printf("%sRegistered cameras:%d%n\n", indent, mCameras.size());
- for (VirtualCameraInfo info : mCameras.values()) {
- VirtualCameraHalConfig config = null;
- try {
- config = info.mVirtualCamera.getHalConfig();
- } catch (RemoteException ex) {
- Log.w(TAG, ex);
- }
- fout.printf(
- "%s- %s isRegistered: %s, token: %s\n",
- indent,
- config == null ? "" : config.displayName,
- info.mIsRegistered,
- info.mVirtualCamera);
+ for (VirtualCameraConfig config : mCameras) {
+ fout.printf("%s token: %s\n", indent, config);
}
}
}
+
+ private void connectVirtualCameraService() {
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ IBinder virtualCameraBinder =
+ ServiceManager.waitForService(VIRTUAL_CAMERA_SERVICE_NAME);
+ if (virtualCameraBinder == null) {
+ Slog.e(TAG, "connectVirtualCameraService: Failed to connect to the virtual "
+ + "camera service");
+ return;
+ }
+ virtualCameraBinder.linkToDeath(this, 0);
+ mVirtualCameraService = IVirtualCameraService.Stub.asInterface(virtualCameraBinder);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ private boolean registerCameraWithService(VirtualCameraConfig config) throws RemoteException {
+ VirtualCameraConfiguration serviceConfiguration = getServiceCameraConfiguration(config);
+ return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
+ serviceConfiguration);
+ }
}
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
new file mode 100644
index 0000000..202f68b
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
@@ -0,0 +1,94 @@
+/*
+ * 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.companion.virtual.camera;
+
+import android.annotation.NonNull;
+import android.companion.virtual.camera.IVirtualCameraCallback;
+import android.companion.virtual.camera.VirtualCameraConfig;
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.companion.virtualcamera.IVirtualCameraService;
+import android.companion.virtualcamera.SupportedStreamConfiguration;
+import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.os.RemoteException;
+import android.view.Surface;
+
+/** Utilities to convert the client side classes to the virtual camera service ones. */
+public final class VirtualCameraConversionUtil {
+
+ /**
+ * Fetches the configuration of the provided virtual cameraConfig that was provided by its owner
+ * and convert it into the {@link IVirtualCameraService} types: {@link
+ * VirtualCameraConfiguration}.
+ *
+ * @param cameraConfig The cameraConfig sent by the client.
+ * @return The converted configuration to be sent to the {@link IVirtualCameraService}.
+ * @throws RemoteException If there was an issue fetching the configuration from the client.
+ */
+ @NonNull
+ public static android.companion.virtualcamera.VirtualCameraConfiguration
+ getServiceCameraConfiguration(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
+ VirtualCameraConfiguration serviceConfiguration = new VirtualCameraConfiguration();
+
+ serviceConfiguration.supportedStreamConfigs =
+ cameraConfig.getStreamConfigs().stream()
+ .map(VirtualCameraConversionUtil::convertSupportedStreamConfiguration)
+ .toArray(SupportedStreamConfiguration[]::new);
+
+ serviceConfiguration.virtualCameraCallback = convertCallback(cameraConfig.getCallback());
+ return serviceConfiguration;
+ }
+
+ @NonNull
+ private static android.companion.virtualcamera.IVirtualCameraCallback convertCallback(
+ @NonNull IVirtualCameraCallback camera) {
+ return new android.companion.virtualcamera.IVirtualCameraCallback.Stub() {
+ @Override
+ public void onStreamConfigured(
+ int streamId, Surface surface, int width, int height, int pixelFormat)
+ throws RemoteException {
+ VirtualCameraStreamConfig streamConfig =
+ createStreamConfig(width, height, pixelFormat);
+ camera.onStreamConfigured(streamId, surface, streamConfig);
+ }
+
+ @Override
+ public void onStreamClosed(int streamId) throws RemoteException {
+ camera.onStreamClosed(streamId);
+ }
+ };
+ }
+
+ @NonNull
+ private static VirtualCameraStreamConfig createStreamConfig(
+ int width, int height, int pixelFormat) {
+ return new VirtualCameraStreamConfig(width, height, pixelFormat);
+ }
+
+ @NonNull
+ private static SupportedStreamConfiguration convertSupportedStreamConfiguration(
+ VirtualCameraStreamConfig stream) {
+ SupportedStreamConfiguration supportedConfig = new SupportedStreamConfiguration();
+ supportedConfig.height = stream.getHeight();
+ supportedConfig.width = stream.getWidth();
+ supportedConfig.pixelFormat = stream.getFormat();
+ return supportedConfig;
+ }
+
+ private VirtualCameraConversionUtil() {
+ }
+}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index cd9c53b..80c4c58 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -41,6 +41,18 @@
"file_patterns": ["StorageManagerService\\.java"]
},
{
+ "name": "CtsScopedStorageBypassDatabaseOperationsTest",
+ "file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
+ "name": "CtsScopedStorageGeneralTest",
+ "file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
+ "name": "CtsScopedStorageRedactUriTest",
+ "file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
"name": "FrameworksMockingServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index bdcde66..f8078d2 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -869,8 +869,7 @@
if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ ", reverting to built-in wallpaper!");
- int which = mWallpaper.mWhich;
- clearWallpaperLocked(which, mWallpaper.userId, false, null);
+ clearWallpaperLocked(mWallpaper.mWhich, mWallpaper.userId, false, null);
}
}
};
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3e23fab..f700944 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -173,7 +173,6 @@
private final TransitionController mController;
private final BLASTSyncEngine mSyncEngine;
private final Token mToken;
- private IApplicationThread mRemoteAnimApp;
private @Nullable ActivityRecord mPipActivity;
@@ -1485,13 +1484,14 @@
return mForcePlaying;
}
+ /** Adjusts the priority of the process which will run the transition animation. */
void setRemoteAnimationApp(IApplicationThread app) {
- mRemoteAnimApp = app;
- }
-
- /** Returns the app which will run the transition animation. */
- IApplicationThread getRemoteAnimationApp() {
- return mRemoteAnimApp;
+ final WindowProcessController wpc = mController.mAtm.getProcessController(app);
+ if (wpc != null) {
+ // This is an early prediction. If the process doesn't ack the animation in 200 ms,
+ // the priority will be restored.
+ mController.mRemotePlayer.update(wpc, true /* running */, true /* predict */);
+ }
}
void setNoAnimation(WindowContainer wc) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 1f01778..a736874 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -1251,13 +1251,7 @@
} else if (mPlayingTransitions.isEmpty()) {
mTransitionPlayerProc.setRunningRemoteAnimation(false);
mRemotePlayer.clear();
- return;
}
- final IApplicationThread appThread = transition.getRemoteAnimationApp();
- if (appThread == null || appThread == mTransitionPlayerProc.getThread()) return;
- final WindowProcessController delegate = mAtm.getProcessController(appThread);
- if (delegate == null) return;
- mRemotePlayer.update(delegate, isPlaying, true /* predict */);
}
/** Called when a transition is aborted. This should only be called by {@link Transition} */
@@ -1483,7 +1477,7 @@
* {@link #mTransitionPlayerProc}.
*/
static class RemotePlayer {
- private static final long REPORT_RUNNING_GRACE_PERIOD_MS = 100;
+ private static final long REPORT_RUNNING_GRACE_PERIOD_MS = 200;
@GuardedBy("itself")
private final ArrayMap<IBinder, DelegateProcess> mDelegateProcesses = new ArrayMap<>();
private final ActivityTaskManagerService mAtm;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java
deleted file mode 100644
index 8f77e9b..0000000
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java
+++ /dev/null
@@ -1,224 +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.server.companion.virtual;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.Manifest;
-import android.companion.virtual.VirtualDeviceParams;
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.IVirtualCameraSession;
-import android.companion.virtual.camera.VirtualCamera;
-import android.companion.virtual.camera.VirtualCameraConfig;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-import android.companion.virtual.camera.VirtualCameraSession;
-import android.companion.virtual.camera.VirtualCameraStreamConfig;
-import android.companion.virtual.flags.Flags;
-import android.content.ComponentName;
-import android.graphics.ImageFormat;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-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.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
-import android.testing.TestableLooper;
-
-import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.AdoptShellPermissionsRule;
-import com.android.server.companion.virtual.camera.IVirtualCameraService;
-import com.android.server.companion.virtual.camera.VirtualCameraController;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashSet;
-import java.util.Set;
-
-@Presubmit
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class VirtualCameraTest {
-
- private static final String PKG = "com.android.virtualcamera";
- private static final String CLS = ".VirtualCameraService";
- public static final String CAMERA_DISPLAY_NAME = "testCamera";
-
- private final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
- private FakeVirtualCameraService mFakeVirtualCameraService;
- private VirtualCameraController mVirtualCameraController;
-
- @Rule public final VirtualDeviceRule mVirtualDeviceRule = new VirtualDeviceRule(mContext);
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
- public AdoptShellPermissionsRule mAdoptShellPermissionsRule =
- new AdoptShellPermissionsRule(
- InstrumentationRegistry.getInstrumentation().getUiAutomation(),
- Manifest.permission.CREATE_VIRTUAL_DEVICE);
-
- @Before
- public void setUp() {
- mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> mVirtualCameraController);
- mFakeVirtualCameraService = new FakeVirtualCameraService();
- connectFakeService();
- mVirtualCameraController = new VirtualCameraController(mContext);
- }
-
- private VirtualDeviceImpl createVirtualDevice() {
- return mVirtualDeviceRule.createVirtualDevice(new VirtualDeviceParams.Builder().build());
- }
-
- private void connectFakeService() {
- mContext.addMockService(
- ComponentName.createRelative(PKG, CLS), mFakeVirtualCameraService.asBinder());
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void addVirtualCamera() {
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
- VirtualCameraConfig config = createVirtualCameraConfig(null);
- IVirtualCamera.Default camera = new IVirtualCamera.Default();
- virtualDevice.registerVirtualCamera(camera);
-
- assertThat(mFakeVirtualCameraService.mCameras).contains(camera);
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void addVirtualCamera_serviceNotReady() {
- TestableContext context =
- new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
- VirtualCameraController virtualCameraController = new VirtualCameraController(context);
- mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> virtualCameraController);
-
- VirtualDeviceImpl virtualDevice =
- mVirtualDeviceRule.createVirtualDevice(new VirtualDeviceParams.Builder().build());
- IVirtualCamera.Default camera = new IVirtualCamera.Default();
- VirtualCameraConfig config = createVirtualCameraConfig(null);
- virtualDevice.registerVirtualCamera(camera);
- FakeVirtualCameraService fakeVirtualCameraService = new FakeVirtualCameraService();
-
- // Only add the service after connecting the camera
- virtualCameraController.onServiceConnected(
- ComponentName.createRelative(PKG, CLS), fakeVirtualCameraService.asBinder());
-
- assertThat(fakeVirtualCameraService.mCameras).contains(camera);
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void getCameraConfiguration() {
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
- VirtualCameraSession virtualCameraSession = new VirtualCameraSession() {};
- VirtualCameraConfig config =
- new VirtualCameraConfig.Builder()
- .addStreamConfiguration(10, 10, ImageFormat.RGB_565)
- .setDisplayName(CAMERA_DISPLAY_NAME)
- .setCallback(
- new HandlerExecutor(new Handler(Looper.getMainLooper())),
- () -> virtualCameraSession)
- .build();
-
- VirtualCamera virtualCamera = new VirtualCamera(virtualDevice, config);
-
- VirtualCameraConfig returnedConfig = virtualCamera.getConfig();
- assertThat(returnedConfig).isNotNull();
- assertThat(returnedConfig.getDisplayName()).isEqualTo(CAMERA_DISPLAY_NAME);
- Set<VirtualCameraStreamConfig> streamConfigs = returnedConfig.getStreamConfigs();
- assertThat(streamConfigs).hasSize(1);
- VirtualCameraStreamConfig streamConfig =
- streamConfigs.toArray(new VirtualCameraStreamConfig[0])[0];
- assertThat(streamConfig.format).isEqualTo(ImageFormat.RGB_565);
- assertThat(streamConfig.width).isEqualTo(10);
- assertThat(streamConfig.height).isEqualTo(10);
-
- VirtualCameraHalConfig halConfig = virtualCamera.getHalConfig();
- assertThat(halConfig).isNotNull();
- assertThat(halConfig.displayName).isEqualTo(CAMERA_DISPLAY_NAME);
- assertThat(halConfig.streamConfigs).asList().hasSize(1);
- assertThat(halConfig.streamConfigs[0].format).isEqualTo(ImageFormat.RGB_565);
- assertThat(halConfig.streamConfigs[0].width).isEqualTo(10);
- assertThat(halConfig.streamConfigs[0].height).isEqualTo(10);
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void createCameraWithVirtualCameraInstance() {
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
-
- VirtualCameraSession virtualCameraSession = new VirtualCameraSession() {};
- VirtualCameraConfig config = createVirtualCameraConfig(virtualCameraSession);
- VirtualCamera virtualCamera = new VirtualCamera(virtualDevice, config);
-
- assertThat(mFakeVirtualCameraService.mCameras).contains(virtualCamera);
- assertThat(virtualCamera.open()).isInstanceOf(IVirtualCameraSession.class);
- }
-
- @RequiresFlagsDisabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void createCameraDoesNothingWhenControllerIsNull() {
- mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> null);
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
- IVirtualCamera.Default camera = new IVirtualCamera.Default();
- VirtualCameraConfig config = createVirtualCameraConfig(null);
- virtualDevice.registerVirtualCamera(camera);
-
- assertThat(mFakeVirtualCameraService.mCameras).doesNotContain(camera);
- }
-
- @NonNull
- private static VirtualCameraConfig createVirtualCameraConfig(
- VirtualCameraSession virtualCameraSession) {
- return new VirtualCameraConfig.Builder()
- .addStreamConfiguration(10, 10, ImageFormat.RGB_565)
- .setDisplayName(CAMERA_DISPLAY_NAME)
- .setCallback(
- new HandlerExecutor(new Handler(Looper.getMainLooper())),
- () -> virtualCameraSession)
- .build();
- }
-
- private static class FakeVirtualCameraService extends IVirtualCameraService.Stub {
-
- final Set<IVirtualCamera> mCameras = new HashSet<>();
-
- @Override
- public boolean registerCamera(IVirtualCamera camera) throws RemoteException {
- mCameras.add(camera);
- return true;
- }
-
- @Override
- public void unregisterCamera(IVirtualCamera camera) throws RemoteException {
- mCameras.remove(camera);
- }
- }
-}
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 f978990..30300ec 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
@@ -1940,7 +1940,7 @@
mRunningAppsChangedCallback,
params,
new DisplayManagerGlobal(mIDisplayManager),
- new VirtualCameraController(mContext));
+ new VirtualCameraController());
mVdms.addVirtualDevice(virtualDeviceImpl);
assertThat(virtualDeviceImpl.getAssociationId()).isEqualTo(mAssociationInfo.getId());
assertThat(virtualDeviceImpl.getPersistentDeviceId())
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
deleted file mode 100644
index dbd6c88..0000000
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
+++ /dev/null
@@ -1,222 +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.server.companion.virtual;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-
-import android.app.admin.DevicePolicyManager;
-import android.companion.AssociationInfo;
-import android.companion.virtual.IVirtualDeviceActivityListener;
-import android.companion.virtual.IVirtualDeviceSoundEffectListener;
-import android.companion.virtual.VirtualDeviceParams;
-import android.companion.virtual.flags.Flags;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.hardware.display.DisplayManagerGlobal;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.IDisplayManager;
-import android.net.MacAddress;
-import android.os.Binder;
-import android.testing.TestableContext;
-import android.util.ArraySet;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.server.LocalServices;
-import com.android.server.companion.virtual.camera.VirtualCameraController;
-import com.android.server.input.InputManagerInternal;
-import com.android.server.sensors.SensorManagerInternal;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Objects;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-/** Test rule to generate instances of {@link VirtualDeviceImpl}. */
-public class VirtualDeviceRule implements TestRule {
-
- private static final int DEVICE_OWNER_UID = 50;
- private static final int VIRTUAL_DEVICE_ID = 42;
-
- private final Context mContext;
- private InputController mInputController;
- private CameraAccessController mCameraAccessController;
- private AssociationInfo mAssociationInfo;
- private VirtualDeviceManagerService mVdms;
- private VirtualDeviceManagerInternal mLocalService;
- private VirtualDeviceLog mVirtualDeviceLog;
-
- // Mocks
- @Mock private InputController.NativeWrapper mNativeWrapperMock;
- @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
- @Mock private IDisplayManager mIDisplayManager;
- @Mock private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
- @Mock private DevicePolicyManager mDevicePolicyManagerMock;
- @Mock private InputManagerInternal mInputManagerInternalMock;
- @Mock private SensorManagerInternal mSensorManagerInternalMock;
- @Mock private IVirtualDeviceActivityListener mActivityListener;
- @Mock private IVirtualDeviceSoundEffectListener mSoundEffectListener;
- @Mock private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
- @Mock private CameraAccessController.CameraAccessBlockedCallback mCameraAccessBlockedCallback;
-
- // Test instance suppliers
- private Supplier<VirtualCameraController> mVirtualCameraControllerSupplier;
-
- /**
- * Create a new {@link VirtualDeviceRule}
- *
- * @param context The context to be used with the rule.
- */
- public VirtualDeviceRule(@NonNull Context context) {
- Objects.requireNonNull(context);
- mContext = context;
- }
-
- /**
- * Sets a supplier that will supply an instance of {@link VirtualCameraController}. If the
- * supplier returns null, a new instance will be created.
- */
- public VirtualDeviceRule withVirtualCameraControllerSupplier(
- Supplier<VirtualCameraController> virtualCameraControllerSupplier) {
- mVirtualCameraControllerSupplier = virtualCameraControllerSupplier;
- return this;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- init(new TestableContext(mContext));
- base.evaluate();
- }
- };
- }
-
- private void init(@NonNull TestableContext context) {
- MockitoAnnotations.initMocks(this);
-
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
-
- doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
- doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
- doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
- LocalServices.removeServiceForTest(InputManagerInternal.class);
- LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
-
- LocalServices.removeServiceForTest(SensorManagerInternal.class);
- LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
-
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.uniqueId = "uniqueId";
- doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
- doReturn(Display.INVALID_DISPLAY).when(mDisplayManagerInternalMock)
- .getDisplayIdToMirror(anyInt());
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
-
- context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManagerMock);
-
- // Allow virtual devices to be created on the looper thread for testing.
- final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
- mInputController =
- new InputController(
- mNativeWrapperMock,
- InstrumentationRegistry.getInstrumentation()
- .getContext()
- .getMainThreadHandler(),
- context.getSystemService(WindowManager.class),
- threadVerifier);
- mCameraAccessController =
- new CameraAccessController(context, mLocalService, mCameraAccessBlockedCallback);
-
- mAssociationInfo =
- new AssociationInfo(
- /* associationId= */ 1,
- 0,
- null,
- null,
- MacAddress.BROADCAST_ADDRESS,
- "",
- null,
- null,
- true,
- false,
- false,
- 0,
- 0,
- -1);
-
- mVdms = new VirtualDeviceManagerService(context);
- mLocalService = mVdms.getLocalServiceInstance();
- mVirtualDeviceLog = new VirtualDeviceLog(context);
- }
-
- /**
- * Create a {@link VirtualDeviceImpl} with the required mocks
- *
- * @param params See {@link
- * android.companion.virtual.VirtualDeviceManager#createVirtualDevice(int,
- * VirtualDeviceParams)}
- */
- public VirtualDeviceImpl createVirtualDevice(VirtualDeviceParams params) {
- VirtualCameraController virtualCameraController = mVirtualCameraControllerSupplier.get();
- if (Flags.virtualCamera()) {
- if (virtualCameraController == null) {
- virtualCameraController = new VirtualCameraController(mContext);
- }
- }
-
- VirtualDeviceImpl virtualDeviceImpl =
- new VirtualDeviceImpl(
- mContext,
- mAssociationInfo,
- mVdms,
- mVirtualDeviceLog,
- new Binder(),
- new AttributionSource(
- DEVICE_OWNER_UID,
- "com.android.virtualdevice.test",
- "virtualdevicerule"),
- VIRTUAL_DEVICE_ID,
- mInputController,
- mCameraAccessController,
- mPendingTrampolineCallback,
- mActivityListener,
- mSoundEffectListener,
- mRunningAppsChangedCallback,
- params,
- new DisplayManagerGlobal(mIDisplayManager),
- virtualCameraController);
- mVdms.addVirtualDevice(virtualDeviceImpl);
- return virtualDeviceImpl;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
new file mode 100644
index 0000000..2583023
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.companion.virtual.camera;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.companion.virtual.camera.VirtualCameraCallback;
+import android.companion.virtual.camera.VirtualCameraConfig;
+import android.companion.virtual.camera.VirtualCameraMetadata;
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.companion.virtualcamera.IVirtualCameraService;
+import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.graphics.ImageFormat;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class VirtualCameraControllerTest {
+
+ private static final int CAMERA_DISPLAY_NAME_RES_ID_1 = 10;
+ private static final int CAMERA_WIDTH_1 = 100;
+ private static final int CAMERA_HEIGHT_1 = 200;
+ private static final int CAMERA_FORMAT_1 = ImageFormat.RGB_565;
+
+ private static final int CAMERA_DISPLAY_NAME_RES_ID_2 = 11;
+ private static final int CAMERA_WIDTH_2 = 400;
+ private static final int CAMERA_HEIGHT_2 = 600;
+ private static final int CAMERA_FORMAT_2 = ImageFormat.YUY2;
+
+ @Mock
+ private IVirtualCameraService mVirtualCameraServiceMock;
+
+ private VirtualCameraController mVirtualCameraController;
+ private final HandlerExecutor mCallbackHandler =
+ new HandlerExecutor(new Handler(Looper.getMainLooper()));
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mVirtualCameraController = new VirtualCameraController(mVirtualCameraServiceMock);
+ when(mVirtualCameraServiceMock.registerCamera(any(), any())).thenReturn(true);
+ }
+
+ @Test
+ public void registerCamera_registersCamera() throws Exception {
+ mVirtualCameraController.registerCamera(createVirtualCameraConfig(
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_DISPLAY_NAME_RES_ID_1));
+
+ ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
+ ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
+ verify(mVirtualCameraServiceMock).registerCamera(any(), configurationCaptor.capture());
+ VirtualCameraConfiguration virtualCameraConfiguration = configurationCaptor.getValue();
+ assertThat(virtualCameraConfiguration.supportedStreamConfigs.length).isEqualTo(1);
+ assertVirtualCameraConfiguration(virtualCameraConfiguration, CAMERA_WIDTH_1,
+ CAMERA_HEIGHT_1, CAMERA_FORMAT_1);
+ }
+
+ @Test
+ public void unregisterCamera_unregistersCamera() throws Exception {
+ VirtualCameraConfig config = createVirtualCameraConfig(
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_DISPLAY_NAME_RES_ID_1);
+ mVirtualCameraController.unregisterCamera(config);
+
+ verify(mVirtualCameraServiceMock).unregisterCamera(any());
+ }
+
+ @Test
+ public void close_unregistersAllCameras() throws Exception {
+ mVirtualCameraController.registerCamera(createVirtualCameraConfig(
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_DISPLAY_NAME_RES_ID_1));
+ mVirtualCameraController.registerCamera(createVirtualCameraConfig(
+ CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_DISPLAY_NAME_RES_ID_2));
+
+ ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
+ ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
+ mVirtualCameraController.close();
+ verify(mVirtualCameraServiceMock, times(2)).registerCamera(any(),
+ configurationCaptor.capture());
+ List<VirtualCameraConfiguration> virtualCameraConfigurations =
+ configurationCaptor.getAllValues();
+ assertThat(virtualCameraConfigurations).hasSize(2);
+ assertVirtualCameraConfiguration(virtualCameraConfigurations.get(0), CAMERA_WIDTH_1,
+ CAMERA_HEIGHT_1, CAMERA_FORMAT_1);
+ assertVirtualCameraConfiguration(virtualCameraConfigurations.get(1), CAMERA_WIDTH_2,
+ CAMERA_HEIGHT_2, CAMERA_FORMAT_2);
+ }
+
+ private VirtualCameraConfig createVirtualCameraConfig(
+ int width, int height, int format, int displayNameResId) {
+ return new VirtualCameraConfig.Builder()
+ .addStreamConfig(width, height, format)
+ .setDisplayNameStringRes(displayNameResId)
+ .setVirtualCameraCallback(mCallbackHandler, createNoOpCallback())
+ .build();
+ }
+
+ private static void assertVirtualCameraConfiguration(
+ VirtualCameraConfiguration configuration, int width, int height, int format) {
+ assertThat(configuration.supportedStreamConfigs[0].width).isEqualTo(width);
+ assertThat(configuration.supportedStreamConfigs[0].height).isEqualTo(height);
+ assertThat(configuration.supportedStreamConfigs[0].pixelFormat).isEqualTo(format);
+ }
+
+ private static VirtualCameraCallback createNoOpCallback() {
+ return new VirtualCameraCallback() {
+
+ @Override
+ public void onStreamConfigured(
+ int streamId,
+ @NonNull Surface surface,
+ @NonNull VirtualCameraStreamConfig streamConfig) {}
+
+ @Override
+ public void onProcessCaptureRequest(
+ int streamId, long frameId, @Nullable VirtualCameraMetadata metadata) {}
+
+ @Override
+ public void onStreamClosed(int streamId) {}
+ };
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index a83caa4..dade3b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -636,6 +636,7 @@
transition.collect(app);
controller.requestStartTransition(transition, null /* startTask */, remoteTransition,
null /* displayChange */);
+ assertTrue(delegateProc.isRunningRemoteTransition());
testPlayer.startTransition();
app.onStartingWindowDrawn();
// The task appeared event should be deferred until transition ready.
@@ -643,7 +644,6 @@
testPlayer.onTransactionReady(app.getSyncTransaction());
assertTrue(task.taskAppearedReady());
assertTrue(playerProc.isRunningRemoteTransition());
- assertTrue(delegateProc.isRunningRemoteTransition());
assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
assertTrue(app.isVisible());