summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 06:59:30 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 06:59:30 +0000
commitbe825ab84cae9db6acc0e27c2d3a9243d08a53b8 (patch)
treebf324e7fbedbda684aa01ba62235e489d9a5f30c
parent55f60ea5a914d06388d4263884e4c97d7251d57b (diff)
parent80987adf91866187c48e2a5d684dd3bbbea76109 (diff)
downloadservices-android13-mainline-cellbroadcast-release.tar.gz
Change-Id: If26244cb59ce3c05decaef4a29189a6c74311d45
-rw-r--r--.gitignore10
-rw-r--r--Android.bp35
-rw-r--r--builtInServices/Android.bp43
-rw-r--r--builtInServices/PREUPLOAD.cfg (renamed from PREUPLOAD.cfg)0
-rw-r--r--builtInServices/TEST_MAPPING10
-rw-r--r--builtInServices/api/current.txt1
-rw-r--r--builtInServices/api/module-lib-current.txt96
-rw-r--r--builtInServices/api/module-lib-removed.txt1
-rw-r--r--builtInServices/api/removed.txt1
-rw-r--r--builtInServices/api/system-current.txt1
-rw-r--r--builtInServices/api/system-removed.txt1
-rw-r--r--builtInServices/api/test-current.txt1
-rw-r--r--builtInServices/api/test-removed.txt1
-rw-r--r--builtInServices/host_tests/Android.bp14
-rw-r--r--builtInServices/host_tests/src/com/android/internal/car/test/CarServiceCrashDumpTest.java237
-rw-r--r--builtInServices/src/com/android/internal/car/CarDevicePolicySafetyChecker.java (renamed from src/com/android/internal/car/CarDevicePolicySafetyChecker.java)6
-rw-r--r--builtInServices/src/com/android/internal/car/CarServiceHelperInterface.java49
-rw-r--r--builtInServices/src/com/android/internal/car/CarServiceHelperService.java (renamed from src/com/android/internal/car/CarServiceHelperService.java)549
-rw-r--r--builtInServices/src/com/android/internal/car/CarServiceHelperServiceUpdatable.java52
-rw-r--r--builtInServices/src/com/android/server/wm/ActivityOptionsWrapper.java63
-rw-r--r--builtInServices/src/com/android/server/wm/ActivityRecordWrapper.java109
-rw-r--r--builtInServices/src/com/android/server/wm/CalculateParams.java134
-rw-r--r--builtInServices/src/com/android/server/wm/CarDisplayAreaPolicyProvider.java140
-rw-r--r--builtInServices/src/com/android/server/wm/CarLaunchParamsModifier.java222
-rw-r--r--builtInServices/src/com/android/server/wm/CarLaunchParamsModifierInterface.java55
-rw-r--r--builtInServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatable.java65
-rw-r--r--builtInServices/src/com/android/server/wm/LaunchParamsWrapper.java104
-rw-r--r--builtInServices/src/com/android/server/wm/RequestWrapper.java49
-rw-r--r--builtInServices/src/com/android/server/wm/TaskDisplayAreaWrapper.java57
-rw-r--r--builtInServices/src/com/android/server/wm/TaskWrapper.java65
-rw-r--r--builtInServices/src/com/android/server/wm/WindowLayoutWrapper.java45
-rw-r--r--builtInServices/tests/Android.mk (renamed from tests/Android.mk)8
-rw-r--r--builtInServices/tests/AndroidManifest.xml (renamed from tests/AndroidManifest.xml)2
-rw-r--r--builtInServices/tests/src/com/android/internal/car/CarDevicePolicySafetyCheckerTest.java (renamed from tests/src/com/android/internal/car/CarDevicePolicySafetyCheckerTest.java)0
-rw-r--r--builtInServices/tests/src/com/android/internal/car/CarServiceHelperServiceTest.java (renamed from tests/src/com/android/internal/car/CarServiceHelperServiceTest.java)132
-rw-r--r--builtInServices/tests/src/com/android/server/wm/ActivityOptionsWrapperTest.java43
-rw-r--r--builtInServices/tests/src/com/android/server/wm/ActivityRecordWrapperTest.java47
-rw-r--r--builtInServices/tests/src/com/android/server/wm/CalculateParamsTest.java74
-rw-r--r--builtInServices/tests/src/com/android/server/wm/LaunchParamsWrapperTest.java39
-rw-r--r--builtInServices/tests/src/com/android/server/wm/RequestWrapperTest.java41
-rw-r--r--builtInServices/tests/src/com/android/server/wm/TaskDisplayAreaWrapperTest.java47
-rw-r--r--builtInServices/tests/src/com/android/server/wm/TaskWrapperTest.java46
-rw-r--r--builtInServices/tests/src/com/android/server/wm/WindowLayoutWrapperTest.java46
-rw-r--r--src/com/android/server/wm/CarLaunchParamsModifier.java490
-rw-r--r--src/jni/com_android_internal_car_CarServiceHelperService.cpp71
-rw-r--r--updatableServices/Android.bp31
-rw-r--r--updatableServices/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImpl.java335
-rw-r--r--updatableServices/src/com/android/internal/car/updatable/CarServiceProxy.java (renamed from src/com/android/internal/car/CarServiceProxy.java)234
-rw-r--r--updatableServices/src/com/android/internal/car/updatable/UserMetrics.java (renamed from src/com/android/internal/car/UserMetrics.java)21
-rw-r--r--updatableServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatableImpl.java440
-rw-r--r--updatableServices/tests/Android.bp50
-rw-r--r--updatableServices/tests/AndroidManifest.xml29
-rw-r--r--updatableServices/tests/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImplTest.java180
-rw-r--r--updatableServices/tests/src/com/android/internal/car/updatable/CarServiceProxyTest.java (renamed from tests/src/com/android/internal/car/CarServiceProxyTest.java)39
-rw-r--r--updatableServices/tests/src/com/android/internal/car/updatable/UserMetricsTest.java (renamed from tests/src/com/android/internal/car/UserMetricsTest.java)6
-rw-r--r--updatableServices/tests/src/com/android/server/wm/CarLaunchParamsModifierUpdatableTest.java (renamed from tests/src/com/android/server/wm/CarLaunchParamsModifierTest.java)226
56 files changed, 3722 insertions, 1171 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b61e3aa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*~
+.project
+.classpath
+*.iml
+gen/
+*.pyc
+*.swp
+__pycache__
+.idea
+/bin/
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index d26195e..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_library {
- name: "car-frameworks-service",
- installable: true,
- libs: [
- "services",
- "android.hardware.automotive.vehicle-V2.0-java",
- "com.android.car.internal.common",
- ],
- required: ["libcar-framework-service-jni"],
- srcs: [
- "src/**/*.java",
- ],
- static_libs: [
- "android.car.watchdoglib",
- "com.android.car.internal.system",
- "android.automotive.watchdog.internal-java",
- ],
-}
-
-cc_library_shared {
- name: "libcar-framework-service-jni",
- shared_libs: [
- "libandroid_runtime",
- "libhidlbase",
- "liblog",
- "libnativehelper",
- "libsuspend",
- "libutils",
- ],
- srcs: ["src/jni/com_android_internal_car_CarServiceHelperService.cpp"],
-}
diff --git a/builtInServices/Android.bp b/builtInServices/Android.bp
new file mode 100644
index 0000000..eeda2da
--- /dev/null
+++ b/builtInServices/Android.bp
@@ -0,0 +1,43 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_sdk_library {
+ name: "car-frameworks-service",
+ libs: [
+ "services",
+ "android.car",
+ "android.car.builtin", // Will remove once split is complete
+ "android.hardware.automotive.vehicle-V2.0-java",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "android.car.watchdoglib",
+ "android.automotive.watchdog.internal-V1-java",
+ ],
+ api_lint: {
+ enabled: true,
+ },
+
+ min_sdk_version: "33",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.car.framework"
+ ],
+
+ unsafe_ignore_missing_latest_api: true,
+
+ test: {
+ enabled: false,
+ },
+ system: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ module_lib: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+}
diff --git a/PREUPLOAD.cfg b/builtInServices/PREUPLOAD.cfg
index 2811ea9..2811ea9 100644
--- a/PREUPLOAD.cfg
+++ b/builtInServices/PREUPLOAD.cfg
diff --git a/builtInServices/TEST_MAPPING b/builtInServices/TEST_MAPPING
new file mode 100644
index 0000000..2832b34
--- /dev/null
+++ b/builtInServices/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "auto-presubmit": [
+ {
+ "name": "CarServiceCrashDumpTest"
+ },
+ {
+ "name": "FrameworkOptCarServicesTest"
+ }
+ ]
+}
diff --git a/builtInServices/api/current.txt b/builtInServices/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/builtInServices/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/builtInServices/api/module-lib-current.txt b/builtInServices/api/module-lib-current.txt
new file mode 100644
index 0000000..ddf15cf
--- /dev/null
+++ b/builtInServices/api/module-lib-current.txt
@@ -0,0 +1,96 @@
+// Signature format: 2.0
+package com.android.internal.car {
+
+ public interface CarServiceHelperInterface {
+ method @Nullable public android.os.UserHandle createUserEvenWhenDisallowed(@Nullable String, @NonNull String, int);
+ method @Nullable public java.io.File dumpServiceStacks();
+ method public void setSafetyMode(boolean);
+ }
+
+ public interface CarServiceHelperServiceUpdatable {
+ method public void dump(@NonNull java.io.PrintWriter, @Nullable String[]);
+ method public com.android.server.wm.CarLaunchParamsModifierUpdatable getCarLaunchParamsModifierUpdatable();
+ method public void initBootUser();
+ method public void onFactoryReset(@NonNull java.util.function.BiConsumer<java.lang.Integer,android.os.Bundle>);
+ method public void onStart();
+ method public void onUserRemoved(@NonNull android.os.UserHandle);
+ method public void sendUserLifecycleEvent(int, @Nullable android.os.UserHandle, @NonNull android.os.UserHandle);
+ }
+
+}
+
+package com.android.server.wm {
+
+ public final class ActivityOptionsWrapper {
+ method public com.android.server.wm.TaskDisplayAreaWrapper getLaunchTaskDisplayArea();
+ method public android.app.ActivityOptions getOptions();
+ }
+
+ public final class ActivityRecordWrapper {
+ method public boolean allowingEmbedded();
+ method public android.content.ComponentName getComponentName();
+ method public com.android.server.wm.TaskDisplayAreaWrapper getDisplayArea();
+ method public int getHandoverLaunchDisplayId();
+ method public com.android.server.wm.TaskDisplayAreaWrapper getHandoverTaskDisplayArea();
+ method public int getUserId();
+ method public boolean isDisplayTrusted();
+ method public boolean isNoDisplay();
+ }
+
+ public final class CalculateParams {
+ method public com.android.server.wm.ActivityRecordWrapper getActivity();
+ method public com.android.server.wm.LaunchParamsWrapper getCurrentParams();
+ method public com.android.server.wm.ActivityOptionsWrapper getOptions();
+ method public com.android.server.wm.LaunchParamsWrapper getOutParams();
+ method public int getPhase();
+ method public com.android.server.wm.RequestWrapper getRequest();
+ method public com.android.server.wm.ActivityRecordWrapper getSource();
+ method public com.android.server.wm.TaskWrapper getTask();
+ method public com.android.server.wm.WindowLayoutWrapper getWindowLayout();
+ method public boolean supportsMultiDisplay();
+ }
+
+ public interface CarLaunchParamsModifierInterface {
+ method @Nullable public com.android.server.wm.TaskDisplayAreaWrapper findTaskDisplayArea(int, int);
+ method @Nullable public com.android.server.wm.TaskDisplayAreaWrapper getDefaultTaskDisplayAreaOnDisplay(int);
+ method @NonNull public java.util.List<com.android.server.wm.TaskDisplayAreaWrapper> getFallbackDisplayAreasForActivity(@NonNull com.android.server.wm.ActivityRecordWrapper, @Nullable com.android.server.wm.RequestWrapper);
+ }
+
+ public interface CarLaunchParamsModifierUpdatable {
+ method public int calculate(com.android.server.wm.CalculateParams);
+ method public android.hardware.display.DisplayManager.DisplayListener getDisplayListener();
+ method public void handleCurrentUserSwitching(int);
+ method public void handleUserStarting(int);
+ method public void handleUserStopped(int);
+ }
+
+ public final class LaunchParamsWrapper {
+ method public android.graphics.Rect getBounds();
+ method public com.android.server.wm.TaskDisplayAreaWrapper getPreferredTaskDisplayArea();
+ method public int getWindowingMode();
+ method public void setBounds(android.graphics.Rect);
+ method public void setPreferredTaskDisplayArea(com.android.server.wm.TaskDisplayAreaWrapper);
+ method public void setWindowingMode(int);
+ field public static int RESULT_CONTINUE;
+ field public static int RESULT_DONE;
+ field public static int RESULT_SKIP;
+ }
+
+ public final class RequestWrapper {
+ }
+
+ public final class TaskDisplayAreaWrapper {
+ method public android.view.Display getDisplay();
+ }
+
+ public final class TaskWrapper {
+ method public com.android.server.wm.TaskWrapper getRootTask();
+ method public com.android.server.wm.TaskDisplayAreaWrapper getTaskDisplayArea();
+ method public int getUserId();
+ }
+
+ public final class WindowLayoutWrapper {
+ }
+
+}
+
diff --git a/builtInServices/api/module-lib-removed.txt b/builtInServices/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/builtInServices/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/builtInServices/api/removed.txt b/builtInServices/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/builtInServices/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/builtInServices/api/system-current.txt b/builtInServices/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/builtInServices/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/builtInServices/api/system-removed.txt b/builtInServices/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/builtInServices/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/builtInServices/api/test-current.txt b/builtInServices/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/builtInServices/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/builtInServices/api/test-removed.txt b/builtInServices/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/builtInServices/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/builtInServices/host_tests/Android.bp b/builtInServices/host_tests/Android.bp
new file mode 100644
index 0000000..9d68572
--- /dev/null
+++ b/builtInServices/host_tests/Android.bp
@@ -0,0 +1,14 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+ name: "CarServiceCrashDumpTest",
+ srcs: ["src/**/CarServiceCrashDumpTest.java"],
+ libs: [
+ "compatibility-host-util",
+ "junit",
+ "tradefed",
+ "truth-prebuilt",
+ ],
+}
diff --git a/builtInServices/host_tests/src/com/android/internal/car/test/CarServiceCrashDumpTest.java b/builtInServices/host_tests/src/com/android/internal/car/test/CarServiceCrashDumpTest.java
new file mode 100644
index 0000000..bf19a3c
--- /dev/null
+++ b/builtInServices/host_tests/src/com/android/internal/car/test/CarServiceCrashDumpTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.car.test;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.compatibility.common.util.CommonTestUtils;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class CarServiceCrashDumpTest extends BaseHostJUnit4Test {
+ private static final int DEFAULT_TIMEOUT_SEC = 20;
+ private static final long POLL_TIMEOUT_MS = 20000;
+ private static final String BUILD_TYPE_PROPERTY = "ro.build.type";
+
+ // This must be in sync with WatchDog lib.
+ private static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
+ "android.hardware.audio@4.0::IDevicesFactory",
+ "android.hardware.audio@5.0::IDevicesFactory",
+ "android.hardware.audio@6.0::IDevicesFactory",
+ "android.hardware.audio@7.0::IDevicesFactory",
+ "android.hardware.biometrics.face@1.0::IBiometricsFace",
+ "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
+ "android.hardware.bluetooth@1.0::IBluetoothHci",
+ "android.hardware.camera.provider@2.4::ICameraProvider",
+ "android.hardware.gnss@1.0::IGnss",
+ "android.hardware.graphics.allocator@2.0::IAllocator",
+ "android.hardware.graphics.composer@2.1::IComposer",
+ "android.hardware.health@2.0::IHealth",
+ "android.hardware.light@2.0::ILight",
+ "android.hardware.media.c2@1.0::IComponentStore",
+ "android.hardware.media.omx@1.0::IOmx",
+ "android.hardware.media.omx@1.0::IOmxStore",
+ "android.hardware.neuralnetworks@1.0::IDevice",
+ "android.hardware.power.stats@1.0::IPowerStats",
+ "android.hardware.sensors@1.0::ISensors",
+ "android.hardware.sensors@2.0::ISensors",
+ "android.hardware.sensors@2.1::ISensors",
+ "android.hardware.vr@1.0::IVr",
+ "android.system.suspend@1.0::ISystemSuspend"
+ );
+
+ // Which native processes to dump into dropbox's stack traces, must be in sync with Watchdog
+ // lib.
+ private static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
+ "/system/bin/audioserver",
+ "/system/bin/cameraserver",
+ "/system/bin/drmserver",
+ "/system/bin/keystore2",
+ "/system/bin/mediadrmserver",
+ "/system/bin/mediaserver",
+ "/system/bin/netd",
+ "/system/bin/sdcard",
+ "/system/bin/surfaceflinger",
+ "/system/bin/vold",
+ "media.extractor", // system/bin/mediaextractor
+ "media.metrics", // system/bin/mediametrics
+ "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
+ "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec
+ "media.transcoding", // Media transcoding service
+ "com.android.bluetooth", // Bluetooth service
+ "/apex/com.android.os.statsd/bin/statsd", // Stats daemon
+ };
+
+ /**
+ * Executes the shell command and returns the output.
+ */
+ private String executeCommand(String command, Object... args) throws Exception {
+ String fullCommand = String.format(command, args);
+ return getDevice().executeShellCommand(fullCommand);
+ }
+
+ /**
+ * Waits until the car service is ready.
+ */
+ private void waitForCarServiceReady() throws Exception {
+ CommonTestUtils.waitUntil("timed out waiting for car service ",
+ DEFAULT_TIMEOUT_SEC, () -> isCarServiceReady());
+ }
+
+ private boolean isCarServiceReady() {
+ String cmd = "service check car_service";
+ try {
+ String output = getDevice().executeShellCommand(cmd).strip();
+ return !output.endsWith("not found");
+ } catch (Exception e) {
+ CLog.w("%s failed: %s", cmd, e.getMessage());
+ }
+ return false;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ executeCommand("logcat -c");
+ }
+
+ /**
+ * Read the content of the dumped file.
+ */
+ private String getDumpFile() throws Exception {
+ AtomicReference<String> log = new AtomicReference<>();
+ String dumpString = "ActivityManager: Dumping to ";
+ String doneDumpingString = "ActivityManager: Done dumping";
+ PollingCheck.check("dumpStackTrace not found in log", POLL_TIMEOUT_MS, () -> {
+ String logString = executeCommand("logcat -d");
+ if (logString.contains("ActivityManager: dumpStackTraces") && logString.contains(
+ dumpString) && logString.contains(doneDumpingString)) {
+ log.set(logString);
+ return true;
+ }
+ return false;
+ });
+ String logString = log.get();
+ int start = logString.indexOf(dumpString) + dumpString.length();
+ int end = logString.indexOf("\n", start);
+ if (end == -1) {
+ end = logString.length();
+ }
+ return logString.substring(start, end);
+ }
+
+ /**
+ * Get a list of PIDs for the interesting HALs that would be dumped.
+ */
+ private List<String> getHalPids() throws Exception {
+ String lshalResult = executeCommand("lshal -i -p");
+ List<String> pids = new ArrayList<String>();
+ int i = 0;
+ for (String line: lshalResult.split("\n")) {
+ line = line.strip();
+ if (line.equals("")) {
+ // When we see an empty line, we stops the parsing.
+ break;
+ }
+ if (i < 2) {
+ // Skip the first two lines
+ i++;
+ continue;
+ }
+ String[] fields = line.split("\\s+");
+ for (String interestHal: HAL_INTERFACES_OF_INTEREST) {
+ if (fields[0].contains(interestHal)) {
+ pids.add(fields[1]);
+ break;
+ }
+ }
+ i++;
+ }
+ return pids;
+ }
+
+ /**
+ * Get a list of PIDs for the native services that would be dumped.
+ */
+ private List<String> getNativePids() throws Exception {
+ List<String> pids = new ArrayList<String>();
+ for (String name: NATIVE_STACKS_OF_INTEREST) {
+ String pid = executeCommand(String.format("pidof %s", name)).strip();
+ if (!pid.equals("")) {
+ pids.add(pid);
+ }
+ }
+ return pids;
+ }
+
+ @Test
+ public void testCarServiceCrashDump() throws Exception {
+ String buildType = getDevice().getProperty("ro.build.type");
+ // Only run on userdebug devices.
+ assumeTrue(buildType.equals("userdebug") || buildType.equals("eng"));
+
+ List<String> pids = new ArrayList<String>();
+
+ getDevice().enableAdbRoot();
+
+ String systemServerPid = executeCommand(String.format("pidof %s", "system_server")).strip();
+ assertWithMessage("system_service pid not empty").that(systemServerPid).isNotEmpty();
+ pids.add(systemServerPid);
+
+ List<String> halPids = getHalPids();
+ pids.addAll(halPids);
+ assertWithMessage("hal pids").that(halPids.size() > 0).isTrue();
+
+ List<String> nativePids = getNativePids();
+ pids.addAll(nativePids);
+ assertWithMessage("native pids").that(nativePids.size() > 0).isTrue();
+
+ executeCommand("am crash --user 0 com.android.car");
+
+ String dumpFile = getDumpFile();
+ assertWithMessage("dump file").that(dumpFile).isNotEmpty();
+
+ String grepResult = executeCommand("cat %s", dumpFile);
+
+ assertWithMessage("dumped content not empty").that(grepResult)
+ .isNotEmpty();
+
+ for (String pid : pids) {
+ assertWithMessage("dumped content contains interesting pid").that(grepResult)
+ .contains(String.format("----- pid %s at", pid));
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ waitForCarServiceReady();
+ }
+}
diff --git a/src/com/android/internal/car/CarDevicePolicySafetyChecker.java b/builtInServices/src/com/android/internal/car/CarDevicePolicySafetyChecker.java
index 0bc0c21..2ae2e63 100644
--- a/src/com/android/internal/car/CarDevicePolicySafetyChecker.java
+++ b/builtInServices/src/com/android/internal/car/CarDevicePolicySafetyChecker.java
@@ -35,12 +35,12 @@ import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
-import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -48,6 +48,8 @@ import java.util.stream.Collectors;
/**
* Integrates {@link android.app.admin.DevicePolicyManager} operations with car UX restrictions.
+ *
+ * @hide
*/
final class CarDevicePolicySafetyChecker {
@@ -123,7 +125,7 @@ final class CarDevicePolicySafetyChecker {
return mSafe.get();
}
- void dump(@NonNull IndentingPrintWriter pw) {
+ void dump(@NonNull PrintWriter pw) {
pw.printf("Safe to run device policy operations: %b\n", mSafe.get());
pw.printf("Unsafe operations: %s\n", Arrays.stream(UNSAFE_OPERATIONS)
.mapToObj(o -> operationToString(o)).collect(Collectors.toList()));
diff --git a/builtInServices/src/com/android/internal/car/CarServiceHelperInterface.java b/builtInServices/src/com/android/internal/car/CarServiceHelperInterface.java
new file mode 100644
index 0000000..362fd07
--- /dev/null
+++ b/builtInServices/src/com/android/internal/car/CarServiceHelperInterface.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.car;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.UserHandle;
+
+import java.io.File;
+
+/**
+ * Interface implemented by CarServiceHelperService.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public interface CarServiceHelperInterface {
+
+ /**
+ * Sets safety mode
+ */
+ void setSafetyMode(boolean safe);
+
+ /**
+ * Creates user even when disallowed
+ */
+ @Nullable
+ UserHandle createUserEvenWhenDisallowed(@Nullable String name, @NonNull String userType,
+ int flags);
+
+ /**
+ * Dumps service stacks
+ */
+ @Nullable
+ File dumpServiceStacks();
+}
diff --git a/src/com/android/internal/car/CarServiceHelperService.java b/builtInServices/src/com/android/internal/car/CarServiceHelperService.java
index 8e82316..4e2333c 100644
--- a/src/com/android/internal/car/CarServiceHelperService.java
+++ b/builtInServices/src/com/android/internal/car/CarServiceHelperService.java
@@ -13,11 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.internal.car;
-import static com.android.car.internal.SystemConstants.ICAR_SYSTEM_SERVER_CLIENT;
-import static com.android.car.internal.common.CommonConstants.CAR_SERVICE_INTERFACE;
+import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STARTING;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPING;
@@ -28,49 +26,36 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.app.admin.DevicePolicySafetyChecker;
import android.automotive.watchdog.internal.ICarWatchdogMonitor;
-import android.automotive.watchdog.internal.PowerCycle;
+import android.automotive.watchdog.internal.ProcessIdentifier;
import android.automotive.watchdog.internal.StateType;
+import android.car.builtin.util.EventLogHelper;
import android.car.watchdoglib.CarWatchdogDaemonHelper;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.pm.UserInfo;
import android.hidl.manager.V1_0.IServiceManager;
-import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.EventLog;
-import android.util.IndentingPrintWriter;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Dumpable;
import android.util.TimeUtils;
-import com.android.car.internal.ICarServiceHelper;
-import com.android.car.internal.ICarSystemServerClient;
import com.android.car.internal.common.CommonConstants.UserLifecycleEventType;
-import com.android.car.internal.common.EventLogTags;
import com.android.car.internal.common.UserHelperLite;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
-import com.android.server.Dumpable;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -80,11 +65,13 @@ import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.CarLaunchParamsModifier;
+import com.android.server.wm.CarLaunchParamsModifierInterface;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -93,24 +80,28 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* System service side companion service for CarService. Starts car service and provide necessary
* API for CarService. Only for car product.
+ *
+ * @hide
*/
public class CarServiceHelperService extends SystemService
- implements Dumpable, DevicePolicySafetyChecker {
+ implements Dumpable, DevicePolicySafetyChecker, CarServiceHelperInterface {
- private static final String TAG = "CarServiceHelper";
+ @VisibleForTesting
+ static final String TAG = "CarServiceHelper";
// TODO(b/154033860): STOPSHIP if they're still true
private static final boolean DBG = true;
private static final boolean VERBOSE = true;
- private static final String PROP_RESTART_RUNTIME = "ro.car.recovery.restart_runtime.enabled";
-
private static final List<String> CAR_HAL_INTERFACES_OF_INTEREST = Arrays.asList(
"android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.automotive.audiocontrol@1.0::IAudioControl",
@@ -121,19 +112,16 @@ public class CarServiceHelperService extends SystemService
private static final int WHAT_POST_PROCESS_DUMPING = 1;
// Message ID representing process killing.
private static final int WHAT_PROCESS_KILL = 2;
- // Message ID representing service unresponsiveness.
- private static final int WHAT_SERVICE_UNRESPONSIVE = 3;
-
- private static final long CAR_SERVICE_BINDER_CALL_TIMEOUT = 15_000;
- private static final long LIFECYCLE_TIMESTAMP_IGNORE = 0;
+ private static final String CSHS_UPDATABLE_CLASSNAME_STRING =
+ "com.android.internal.car.updatable.CarServiceHelperServiceUpdatableImpl";
+ private static final String PROC_PID_STAT_PATTERN =
+ "(?<pid>[0-9]*)\\s\\((?<name>\\S+)\\)\\s\\S\\s(?:-?[0-9]*\\s){18}"
+ + "(?<startClockTicks>[0-9]*)\\s(?:-?[0-9]*\\s)*-?[0-9]*";
- private final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();
private final Context mContext;
private final Object mLock = new Object();
@GuardedBy("mLock")
- private IBinder mCarServiceBinder;
- @GuardedBy("mLock")
private boolean mSystemBootCompleted;
private final CarLaunchParamsModifier mCarLaunchParamsModifier;
@@ -142,14 +130,8 @@ public class CarServiceHelperService extends SystemService
private final HandlerThread mHandlerThread = new HandlerThread("CarServiceHelperService");
private final ProcessTerminator mProcessTerminator = new ProcessTerminator();
- private final CarServiceConnectedCallback mCarServiceConnectedCallback =
- new CarServiceConnectedCallback();
- private final CarServiceProxy mCarServiceProxy;
- /**
- * End-to-end time (from process start) for unlocking the first non-system user.
- */
- private long mFirstUnlockedUserDuration;
+ private final Pattern mProcPidStatPattern = Pattern.compile(PROC_PID_STAT_PATTERN);
private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
private final ICarWatchdogMonitorImpl mCarWatchdogMonitor = new ICarWatchdogMonitorImpl(this);
@@ -160,52 +142,21 @@ public class CarServiceHelperService extends SystemService
}
};
- private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
- if (DBG) {
- Slogf.d(TAG, "onServiceConnected: %s", iBinder);
- }
- handleCarServiceConnection(iBinder);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- handleCarServiceCrash();
- }
- };
+ private final CarDevicePolicySafetyChecker mCarDevicePolicySafetyChecker;
- private final BroadcastReceiver mShutdownEventReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Skip immediately if intent is not relevant to device shutdown.
- // FLAG_RECEIVER_FOREGROUND is checked to ignore the intent from UserController when
- // a user is stopped.
- if ((!intent.getAction().equals(Intent.ACTION_REBOOT)
- && !intent.getAction().equals(Intent.ACTION_SHUTDOWN))
- || (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) == 0) {
- return;
- }
- int powerCycle = PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER;
- try {
- mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.POWER_CYCLE,
- powerCycle, /* arg2= */ 0);
- if (DBG) {
- Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle);
- }
- } catch (RemoteException | RuntimeException e) {
- Slogf.w(TAG, "Notifying power cycle state change failed: %s", e);
- }
- }
- };
+ private CarServiceHelperServiceUpdatable mCarServiceHelperServiceUpdatable;
- private final CarDevicePolicySafetyChecker mCarDevicePolicySafetyChecker;
+ /**
+ * End-to-end time (from process start) for unlocking the first non-system user.
+ */
+ private long mFirstUnlockedUserDuration;
public CarServiceHelperService(Context context) {
this(context,
new CarLaunchParamsModifier(context),
new CarWatchdogDaemonHelper(TAG),
- null
+ /* carServiceHelperServiceUpdatable= */ null,
+ /* carDevicePolicySafetyChecker= */ null
);
}
@@ -214,7 +165,8 @@ public class CarServiceHelperService extends SystemService
Context context,
CarLaunchParamsModifier carLaunchParamsModifier,
CarWatchdogDaemonHelper carWatchdogDaemonHelper,
- CarServiceProxy carServiceOperationManager) {
+ @Nullable CarServiceHelperServiceUpdatable carServiceHelperServiceUpdatable,
+ @Nullable CarDevicePolicySafetyChecker carDevicePolicySafetyChecker) {
super(context);
mContext = context;
@@ -222,9 +174,31 @@ public class CarServiceHelperService extends SystemService
mHandler = new Handler(mHandlerThread.getLooper());
mCarLaunchParamsModifier = carLaunchParamsModifier;
mCarWatchdogDaemonHelper = carWatchdogDaemonHelper;
- mCarServiceProxy =
- carServiceOperationManager == null ? new CarServiceProxy(this)
- : carServiceOperationManager;
+ try {
+ if (carServiceHelperServiceUpdatable == null) {
+ mCarServiceHelperServiceUpdatable = (CarServiceHelperServiceUpdatable) Class
+ .forName(CSHS_UPDATABLE_CLASSNAME_STRING)
+ .getConstructor(Context.class, CarServiceHelperInterface.class,
+ CarLaunchParamsModifierInterface.class)
+ .newInstance(mContext, this,
+ mCarLaunchParamsModifier.getBuiltinInterface());
+ Slogf.d(TAG, "CarServiceHelperServiceUpdatable created via reflection.");
+ } else {
+ mCarServiceHelperServiceUpdatable = carServiceHelperServiceUpdatable;
+ }
+ } catch (Exception e) {
+ // TODO(b/190458000): Define recovery mechanism.
+ // can't create the CarServiceHelperServiceUpdatable object
+ // crash the process
+ Slogf.w(TAG, e, "*** CARHELPER KILLING SYSTEM PROCESS: "
+ + "Can't create CarServiceHelperServiceUpdatable.");
+ Slogf.w(TAG, "*** GOODBYE!");
+ Process.killProcess(Process.myPid());
+ System.exit(10);
+ }
+ mCarLaunchParamsModifier.setUpdatable(
+ mCarServiceHelperServiceUpdatable.getCarLaunchParamsModifierUpdatable());
+
UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
if (umi != null) {
umi.addUserLifecycleListener(new UserLifecycleListener() {
@@ -235,17 +209,20 @@ public class CarServiceHelperService extends SystemService
@Override
public void onUserRemoved(UserInfo user) {
if (DBG) Slogf.d(TAG, "onUserRemoved(): $s", user.toFullString());
- mCarServiceProxy.onUserRemoved(user);
+ mCarServiceHelperServiceUpdatable.onUserRemoved(user.getUserHandle());
}
});
} else {
Slogf.e(TAG, "UserManagerInternal not available - should only happen on unit tests");
}
- mCarDevicePolicySafetyChecker = new CarDevicePolicySafetyChecker(this);
+ mCarDevicePolicySafetyChecker = carDevicePolicySafetyChecker == null
+ ? new CarDevicePolicySafetyChecker(this)
+ : carDevicePolicySafetyChecker;
}
+
@Override
public void onBootPhase(int phase) {
- EventLog.writeEvent(EventLogTags.CAR_HELPER_BOOT_PHASE, phase);
+ EventLogHelper.writeCarHelperBootPhase(phase);
if (DBG) Slogf.d(TAG, "onBootPhase: %d", phase);
TimingsTraceAndSlog t = newTimingsTraceAndSlog();
@@ -271,40 +248,26 @@ public class CarServiceHelperService extends SystemService
@Override
public void onStart() {
- EventLog.writeEvent(EventLogTags.CAR_HELPER_START);
+ EventLogHelper.writeCarHelperStart();
- IntentFilter filter = new IntentFilter(Intent.ACTION_REBOOT);
- filter.addAction(Intent.ACTION_SHUTDOWN);
- mContext.registerReceiverForAllUsers(mShutdownEventReceiver, filter, null, null);
mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener);
mCarWatchdogDaemonHelper.connect();
- Intent intent = new Intent();
- intent.setPackage("com.android.car");
- intent.setAction(CAR_SERVICE_INTERFACE);
- if (!mContext.bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,
- mHandler, UserHandle.SYSTEM)) {
- Slogf.wtf(TAG, "cannot start car service");
- }
- loadNativeLibrary();
+ mCarServiceHelperServiceUpdatable.onStart();
}
@Override
- public void dump(IndentingPrintWriter pw, String[] args) {
+ public void dump(PrintWriter pw, String[] args) {
+ // Usage: adb shell dumpsys system_server_dumper --name CarServiceHelper
if (args == null || args.length == 0 || args[0].equals("-a")) {
pw.printf("System boot completed: %b\n", mSystemBootCompleted);
pw.print("First unlocked user duration: ");
TimeUtils.formatDuration(mFirstUnlockedUserDuration, pw); pw.println();
pw.printf("Queued tasks: %d\n", mProcessTerminator.mQueuedTask);
- mCarServiceProxy.dump(pw);
+ mCarServiceHelperServiceUpdatable.dump(pw, args);
mCarDevicePolicySafetyChecker.dump(pw);
return;
}
- if ("--user-metrics-only".equals(args[0])) {
- mCarServiceProxy.dumpUserMetrics(pw);
- return;
- }
-
if ("--is-operation-safe".equals(args[0]) & args.length > 1) {
String arg1 = args[1];
int operation = 0;
@@ -323,6 +286,12 @@ public class CarServiceHelperService extends SystemService
DevicePolicyManager.operationSafetyReasonToString(reason));
return;
}
+
+ if ("--user-metrics-only".equals(args[0]) || "--dump-service-stacks".equals(args[0])) {
+ mCarServiceHelperServiceUpdatable.dump(pw, args);
+ return;
+ }
+
pw.printf("Invalid args: %s\n", Arrays.toString(args));
}
@@ -334,17 +303,19 @@ public class CarServiceHelperService extends SystemService
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
if (isPreCreated(user, USER_LIFECYCLE_EVENT_TYPE_UNLOCKING)) return;
- EventLog.writeEvent(EventLogTags.CAR_HELPER_USER_UNLOCKING, user.getUserIdentifier());
+ EventLogHelper.writeCarHelperUserUnlocking(user.getUserIdentifier());
if (DBG) Slogf.d(TAG, "onUserUnlocking(%s)", user);
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING, user);
+ mCarServiceHelperServiceUpdatable
+ .sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING,
+ /* userFrom= */ null, user.getUserHandle());
}
@Override
public void onUserUnlocked(@NonNull TargetUser user) {
if (isPreCreated(user, USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)) return;
int userId = user.getUserIdentifier();
- EventLog.writeEvent(EventLogTags.CAR_HELPER_USER_UNLOCKED, userId);
+ EventLogHelper.writeCarHelperUserUnlocked(userId);
if (DBG) Slogf.d(TAG, "onUserUnlocked(%s)", user);
if (mFirstUnlockedUserDuration == 0 && !UserHelperLite.isHeadlessSystemUser(userId)) {
@@ -353,25 +324,30 @@ public class CarServiceHelperService extends SystemService
Slogf.i(TAG, "Time to unlock 1st user(%s): %s", user,
TimeUtils.formatDuration(mFirstUnlockedUserDuration));
}
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, user);
+ mCarServiceHelperServiceUpdatable.sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED,
+ /* userFrom= */ null, user.getUserHandle());
}
@Override
public void onUserStarting(@NonNull TargetUser user) {
if (isPreCreated(user, USER_LIFECYCLE_EVENT_TYPE_STARTING)) return;
- EventLog.writeEvent(EventLogTags.CAR_HELPER_USER_STARTING, user.getUserIdentifier());
+ EventLogHelper.writeCarHelperUserStarting(user.getUserIdentifier());
if (DBG) Slogf.d(TAG, "onUserStarting(%s)", user);
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_STARTING, user);
+ mCarServiceHelperServiceUpdatable.sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_STARTING,
+ /* userFrom= */ null, user.getUserHandle());
+ int userId = user.getUserIdentifier();
+ mCarLaunchParamsModifier.handleUserStarting(userId);
}
@Override
public void onUserStopping(@NonNull TargetUser user) {
if (isPreCreated(user, USER_LIFECYCLE_EVENT_TYPE_STOPPING)) return;
- EventLog.writeEvent(EventLogTags.CAR_HELPER_USER_STOPPING, user.getUserIdentifier());
+ EventLogHelper.writeCarHelperUserStopping(user.getUserIdentifier());
if (DBG) Slogf.d(TAG, "onUserStopping(%s)", user);
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_STOPPING, user);
+ mCarServiceHelperServiceUpdatable.sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_STOPPING,
+ /* userFrom= */ null, user.getUserHandle());
int userId = user.getUserIdentifier();
mCarLaunchParamsModifier.handleUserStopped(userId);
}
@@ -379,26 +355,45 @@ public class CarServiceHelperService extends SystemService
@Override
public void onUserStopped(@NonNull TargetUser user) {
if (isPreCreated(user, USER_LIFECYCLE_EVENT_TYPE_STOPPED)) return;
- EventLog.writeEvent(EventLogTags.CAR_HELPER_USER_STOPPED, user.getUserIdentifier());
+ EventLogHelper.writeCarHelperUserStopped(user.getUserIdentifier());
if (DBG) Slogf.d(TAG, "onUserStopped(%s)", user);
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_STOPPED, user);
+ mCarServiceHelperServiceUpdatable.sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_STOPPED,
+ /* userFrom= */ null, user.getUserHandle());
}
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
if (isPreCreated(to, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) return;
- EventLog.writeEvent(EventLogTags.CAR_HELPER_USER_SWITCHING,
+ EventLogHelper.writeCarHelperUserSwitching(
from == null ? UserHandle.USER_NULL : from.getUserIdentifier(),
to.getUserIdentifier());
if (DBG) Slogf.d(TAG, "onUserSwitching(%s>>%s)", from, to);
- mCarServiceProxy.sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
- from, to);
+ mCarServiceHelperServiceUpdatable.sendUserLifecycleEvent(
+ USER_LIFECYCLE_EVENT_TYPE_SWITCHING, from.getUserHandle(),
+ to.getUserHandle());
int userId = to.getUserIdentifier();
mCarLaunchParamsModifier.handleCurrentUserSwitching(userId);
}
+ @Override
+ public void onUserCompletedEvent(TargetUser user, UserCompletedEventType eventType) {
+ if (user.isPreCreated()) {
+ if (DBG) {
+ Slogf.d(TAG, "Ignoring USER_COMPLETED event %s for pre-created user %s",
+ eventType, user);
+ }
+ return;
+ }
+
+ UserHandle handle = user.getUserHandle();
+ if (eventType.includesOnUserUnlocked()) {
+ mCarServiceHelperServiceUpdatable.sendUserLifecycleEvent(
+ USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, /* userFrom= */ null, handle);
+ }
+ }
+
@Override // from DevicePolicySafetyChecker
@OperationSafetyReason
public int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
@@ -415,13 +410,16 @@ public class CarServiceHelperService extends SystemService
@Override // from DevicePolicySafetyChecker
public void onFactoryReset(IResultReceiver callback) {
if (DBG) Slogf.d(TAG, "onFactoryReset: %s", callback);
-
- mCarServiceProxy.onFactoryReset(callback);
- }
-
- @VisibleForTesting
- void loadNativeLibrary() {
- System.loadLibrary("car-framework-service-jni");
+ if (callback != null) {
+ mCarServiceHelperServiceUpdatable.onFactoryReset((resultCode, resultData) -> {
+ try {
+ callback.send(resultCode, resultData);
+ } catch (RemoteException e) {
+ Slogf.w(TAG, e,
+ "Callback to DevicePolicySafetyChecker threw RemoteException");
+ }
+ });
+ }
}
private boolean isPreCreated(@NonNull TargetUser user, @UserLifecycleEventType int eventType) {
@@ -433,25 +431,6 @@ public class CarServiceHelperService extends SystemService
return true;
}
- @VisibleForTesting
- void handleCarServiceConnection(IBinder iBinder) {
- synchronized (mLock) {
- if (mCarServiceBinder == iBinder) {
- return; // already connected.
- }
- Slogf.i(TAG, "car service binder changed, was %s new: %s", mCarServiceBinder, iBinder);
- mCarServiceBinder = iBinder;
- Slogf.i(TAG, "**CarService connected**");
- }
-
- sendSetSystemServerConnectionsCall();
-
- mHandler.removeMessages(WHAT_SERVICE_UNRESPONSIVE);
- mHandler.sendMessageDelayed(
- obtainMessage(CarServiceHelperService::handleCarServiceUnresponsive, this)
- .setWhat(WHAT_SERVICE_UNRESPONSIVE), CAR_SERVICE_BINDER_CALL_TIMEOUT);
- }
-
private TimingsTraceAndSlog newTimingsTraceAndSlog() {
return new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
}
@@ -459,50 +438,10 @@ public class CarServiceHelperService extends SystemService
private void setupAndStartUsers(@NonNull TimingsTraceAndSlog t) {
// TODO(b/156263735): decide if it should return in case the device's on Retail Mode
t.traceBegin("setupAndStartUsers");
- mCarServiceProxy.initBootUser();
+ mCarServiceHelperServiceUpdatable.initBootUser();
t.traceEnd();
}
- private void handleCarServiceUnresponsive() {
- // This should not happen. Calling this method means ICarSystemServerClient binder is not
- // returned after service connection. and CarService has not connected in the given time.
- Slogf.w(TAG, "*** CARHELPER KILLING SYSTEM PROCESS: CarService unresponsive.");
- Slogf.w(TAG, "*** GOODBYE!");
- Process.killProcess(Process.myPid());
- System.exit(10);
- }
-
- private void sendSetSystemServerConnectionsCall() {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(CAR_SERVICE_INTERFACE);
- data.writeStrongBinder(mHelper.asBinder());
- data.writeStrongBinder(mCarServiceConnectedCallback.asBinder());
- IBinder binder;
- synchronized (mLock) {
- binder = mCarServiceBinder;
- }
- int code = IBinder.FIRST_CALL_TRANSACTION;
- try {
- if (VERBOSE) Slogf.v(TAG, "calling one-way binder transaction with code %d", code);
- // oneway void setSystemServerConnections(in IBinder helper, in IBinder receiver) = 0;
- binder.transact(code, data, null, Binder.FLAG_ONEWAY);
- if (VERBOSE) Slogf.v(TAG, "finished one-way binder transaction with code %d", code);
- } catch (RemoteException e) {
- Slogf.w(TAG, "RemoteException from car service", e);
- handleCarServiceCrash();
- } catch (RuntimeException e) {
- Slogf.wtf(TAG, e, "Exception calling binder transaction (real code: %d)", code);
- throw e;
- } finally {
- data.recycle();
- }
- }
-
- private void sendUserLifecycleEvent(@UserLifecycleEventType int eventType,
- @NonNull TargetUser user) {
- mCarServiceProxy.sendUserLifecycleEvent(eventType, /* from= */ null, user);
- }
-
// Adapted from frameworks/base/services/core/java/com/android/server/Watchdog.java
// TODO(b/131861630) use implementation common with Watchdog.java
//
@@ -546,60 +485,55 @@ public class CarServiceHelperService extends SystemService
return pids;
}
+ /**
+ * Dumps service stack
+ */
// Borrowed from Watchdog.java. Create an ANR file from the call stacks.
- //
- private static void dumpServiceStacks() {
+ @Override
+ @Nullable
+ public File dumpServiceStacks() {
ArrayList<Integer> pids = new ArrayList<>();
pids.add(Process.myPid());
- ActivityManagerService.dumpStackTraces(
+ return ActivityManagerService.dumpStackTraces(
pids, null, null, getInterestingNativePids(), null);
}
- @VisibleForTesting
- void handleCarServiceCrash() {
- // Recovery behavior. Kill the system server and reset
- // everything if enabled by the property.
- boolean restartOnServiceCrash = SystemProperties.getBoolean(PROP_RESTART_RUNTIME, false);
-
- mHandler.removeMessages(WHAT_SERVICE_UNRESPONSIVE);
-
- dumpServiceStacks();
- if (restartOnServiceCrash) {
- Slogf.w(TAG, "*** CARHELPER KILLING SYSTEM PROCESS: CarService crash");
- Slogf.w(TAG, "*** GOODBYE!");
- Process.killProcess(Process.myPid());
- System.exit(10);
- } else {
- Slogf.w(TAG, "*** CARHELPER ignoring: CarService crash");
- }
- }
-
- private void handleClientsNotResponding(@NonNull int[] pids) {
- mProcessTerminator.requestTerminateProcess(pids);
+ private void handleClientsNotResponding(@NonNull List<ProcessIdentifier> processIdentifiers) {
+ mProcessTerminator.requestTerminateProcess(processIdentifiers);
}
private void registerMonitorToWatchdogDaemon() {
try {
mCarWatchdogDaemonHelper.registerMonitor(mCarWatchdogMonitor);
+ synchronized (mLock) {
+ if (!mSystemBootCompleted) {
+ return;
+ }
+ }
+ mCarWatchdogDaemonHelper.notifySystemStateChange(
+ StateType.BOOT_PHASE, SystemService.PHASE_BOOT_COMPLETED, /* arg2= */ 0);
} catch (RemoteException | RuntimeException e) {
Slogf.w(TAG, "Cannot register to car watchdog daemon: %s", e);
}
}
- private void killProcessAndReportToMonitor(int pid) {
- String processName = getProcessName(pid);
- Process.killProcess(pid);
- Slogf.w(TAG, "carwatchdog killed %s (pid: %d)", processName, pid);
+ private void killProcessAndReportToMonitor(ProcessIdentifier processIdentifier) {
+ ProcessInfo processInfo = getProcessInfo(processIdentifier.pid);
+ if (!processInfo.doMatch(processIdentifier.pid, processIdentifier.startTimeMillis)) {
+ return;
+ }
+ String cmdline = getProcessCmdLine(processIdentifier.pid);
+ Process.killProcess(processIdentifier.pid);
+ Slogf.w(TAG, "carwatchdog killed %s %s", cmdline, processInfo);
try {
- mCarWatchdogDaemonHelper.tellDumpFinished(mCarWatchdogMonitor, pid);
+ mCarWatchdogDaemonHelper.tellDumpFinished(mCarWatchdogMonitor, processIdentifier);
} catch (RemoteException | RuntimeException e) {
Slogf.w(TAG, "Cannot report monitor result to car watchdog daemon: %s", e);
}
}
- private static String getProcessName(int pid) {
- String unknownProcessName = "unknown process";
+ private static String getProcessCmdLine(int pid) {
String filename = "/proc/" + pid + "/cmdline";
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line = reader.readLine().replace('\0', ' ').trim();
@@ -608,74 +542,53 @@ public class CarServiceHelperService extends SystemService
line = line.substring(0, index);
}
return Paths.get(line).getFileName().toString();
- } catch (IOException e) {
+ } catch (IOException | RuntimeException e) {
Slogf.w(TAG, "Cannot read %s", filename);
- return unknownProcessName;
+ return ProcessInfo.UNKNOWN_PROCESS;
}
}
- private static native int nativeForceSuspend(int timeoutMs);
-
- // TODO(b/173664653): it's missing unit tests (for example, to make sure that
- // when its setSafetyMode() is called, mCarDevicePolicySafetyChecker is updated).
- private class ICarServiceHelperImpl extends ICarServiceHelper.Stub {
- /**
- * Force device to suspend
- */
- @Override // Binder call
- public int forceSuspend(int timeoutMs) {
- int retVal;
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- final long ident = Binder.clearCallingIdentity();
- try {
- retVal = nativeForceSuspend(timeoutMs);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ private ProcessInfo getProcessInfo(int pid) {
+ String filename = "/proc/" + pid + "/stat";
+ try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
+ String line = reader.readLine().replace('\0', ' ').trim();
+ Matcher m = mProcPidStatPattern.matcher(line);
+ if (m.find()) {
+ int readPid = Integer.parseInt(Objects.requireNonNull(m.group("pid")));
+ if (readPid == pid) {
+ return new ProcessInfo(pid, m.group("name"),
+ Long.parseLong(Objects.requireNonNull(m.group("startClockTicks"))));
+ }
}
- return retVal;
- }
-
- @Override
- public void setDisplayAllowlistForUser(@UserIdInt int userId, int[] displayIds) {
- mCarLaunchParamsModifier.setDisplayAllowListForUser(userId, displayIds);
- }
-
- @Override
- public void setPassengerDisplays(int[] displayIdsForPassenger) {
- mCarLaunchParamsModifier.setPassengerDisplays(displayIdsForPassenger);
+ } catch (IOException | RuntimeException e) {
+ Slogf.w(TAG, e, "Cannot read %s", filename);
}
+ return new ProcessInfo(pid, ProcessInfo.UNKNOWN_PROCESS, ProcessInfo.INVALID_START_TIME);
+ }
- @Override
- public void setSourcePreferredComponents(boolean enableSourcePreferred,
- @Nullable List<ComponentName> sourcePreferredComponents) {
- mCarLaunchParamsModifier.setSourcePreferredComponents(
- enableSourcePreferred, sourcePreferredComponents);
- }
+ @Override
+ public void setSafetyMode(boolean safe) {
+ mCarDevicePolicySafetyChecker.setSafe(safe);
+ }
- @Override
- public void setSafetyMode(boolean safe) {
- mCarDevicePolicySafetyChecker.setSafe(safe);
+ @Override
+ public UserHandle createUserEvenWhenDisallowed(String name, String userType, int flags) {
+ if (DBG) {
+ Slogf.d(TAG, "createUserEvenWhenDisallowed(): name=%s, type=%s, flags=%s",
+ UserHelperLite.safeName(name), userType, UserInfo.flagsToString(flags));
}
-
- @Override
- public UserInfo createUserEvenWhenDisallowed(String name, String userType, int flags) {
+ UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ try {
+ UserInfo user = umi.createUserEvenWhenDisallowed(name, userType, flags,
+ /* disallowedPackages= */ null, /* token= */ null);
if (DBG) {
- Slogf.d(TAG, "createUserEvenWhenDisallowed(): name=%s, type=%s, flags=%s",
- UserHelperLite.safeName(name), userType, UserInfo.flagsToString(flags));
- }
- UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
- try {
- UserInfo user = umi.createUserEvenWhenDisallowed(name, userType, flags,
- /* disallowedPackages= */ null, /* token= */ null);
- if (DBG) {
- Slogf.d(TAG, "User created: %s", (user == null ? "null" : user.toFullString()));
- }
- // TODO(b/172691310): decide if user should be affiliated when DeviceOwner is set
- return user;
- } catch (UserManager.CheckedUserOperationException e) {
- Slogf.e(TAG, "Error creating user", e);
- return null;
+ Slogf.d(TAG, "User created: %s", (user == null ? "null" : user.toFullString()));
}
+ // TODO(b/172691310): decide if user should be affiliated when DeviceOwner is set
+ return user.getUserHandle();
+ } catch (UserManager.CheckedUserOperationException e) {
+ Slogf.e(TAG, e, "Error creating user");
+ return null;
}
}
@@ -687,12 +600,22 @@ public class CarServiceHelperService extends SystemService
}
@Override
- public void onClientsNotResponding(int[] pids) {
+ public void onClientsNotResponding(List<ProcessIdentifier> processIdentifiers) {
CarServiceHelperService service = mService.get();
- if (service == null || pids == null || pids.length == 0) {
+ if (service == null || processIdentifiers == null || processIdentifiers.isEmpty()) {
return;
}
- service.handleClientsNotResponding(pids);
+ service.handleClientsNotResponding(processIdentifiers);
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return ICarWatchdogMonitor.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return ICarWatchdogMonitor.VERSION;
}
}
@@ -705,7 +628,7 @@ public class CarServiceHelperService extends SystemService
@GuardedBy("mProcessLock")
private int mQueuedTask;
- public void requestTerminateProcess(@NonNull int[] pids) {
+ public void requestTerminateProcess(@NonNull List<ProcessIdentifier> processIdentifiers) {
synchronized (mProcessLock) {
// If there is a running thread, we re-use it instead of starting a new thread.
if (mExecutor == null) {
@@ -714,8 +637,13 @@ public class CarServiceHelperService extends SystemService
mQueuedTask++;
}
mExecutor.execute(() -> {
- for (int pid : pids) {
- dumpAndKillProcess(pid);
+ for (int i = 0; i < processIdentifiers.size(); i++) {
+ ProcessIdentifier processIdentifier = processIdentifiers.get(i);
+ ProcessInfo processInfo = getProcessInfo(processIdentifier.pid);
+ if (processInfo.doMatch(processIdentifier.pid,
+ processIdentifier.startTimeMillis)) {
+ dumpAndKillProcess(processIdentifier);
+ }
}
// mExecutor will be stopped from the main thread, if there is no queued task.
mHandler.sendMessage(obtainMessage(ProcessTerminator::postProcessing, this)
@@ -733,17 +661,17 @@ public class CarServiceHelperService extends SystemService
}
}
- private void dumpAndKillProcess(int pid) {
+ private void dumpAndKillProcess(ProcessIdentifier processIdentifier) {
if (DBG) {
- Slogf.d(TAG, "Dumping and killing process(pid: %d)", pid);
+ Slogf.d(TAG, "Dumping and killing process(pid: %d)", processIdentifier.pid);
}
ArrayList<Integer> javaPids = new ArrayList<>(1);
ArrayList<Integer> nativePids = new ArrayList<>();
try {
- if (isJavaApp(pid)) {
- javaPids.add(pid);
+ if (isJavaApp(processIdentifier.pid)) {
+ javaPids.add(processIdentifier.pid);
} else {
- nativePids.add(pid);
+ nativePids.add(processIdentifier.pid);
}
} catch (IOException e) {
Slogf.w(TAG, "Cannot get process information: %s", e);
@@ -760,10 +688,10 @@ public class CarServiceHelperService extends SystemService
if (dumpTime < ONE_SECOND_MS) {
mHandler.sendMessageDelayed(obtainMessage(
CarServiceHelperService::killProcessAndReportToMonitor,
- CarServiceHelperService.this, pid).setWhat(WHAT_PROCESS_KILL),
+ CarServiceHelperService.this, processIdentifier).setWhat(WHAT_PROCESS_KILL),
ONE_SECOND_MS - dumpTime);
} else {
- killProcessAndReportToMonitor(pid);
+ killProcessAndReportToMonitor(processIdentifier);
}
}
@@ -772,25 +700,44 @@ public class CarServiceHelperService extends SystemService
String target = Files.readSymbolicLink(exePath).toString();
// Zygote's target exe is also /system/bin/app_process32 or /system/bin/app_process64.
// But, we can be very sure that Zygote will not be the client of car watchdog daemon.
- return target == "/system/bin/app_process32" || target == "/system/bin/app_process64";
+ return target.equals("/system/bin/app_process32") ||
+ target.equals("/system/bin/app_process64");
}
}
- private final class CarServiceConnectedCallback extends IResultReceiver.Stub {
- @Override
- public void send(int resultCode, Bundle resultData) {
- mHandler.removeMessages(WHAT_SERVICE_UNRESPONSIVE);
-
- IBinder binder;
- if (resultData == null
- || (binder = resultData.getBinder(ICAR_SYSTEM_SERVER_CLIENT)) == null) {
- Slogf.wtf(TAG, "setSystemServerConnections return NULL Binder.");
- handleCarServiceUnresponsive();
- return;
- }
+ private static final class ProcessInfo {
+ public static final String UNKNOWN_PROCESS = "unknown process";
+ public static final int INVALID_START_TIME = -1;
+
+ private static final long MILLIS_PER_JIFFY = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK);
+
+ public final int pid;
+ public final String name;
+ public final long startTimeMillis;
+
+ ProcessInfo(int pid, String name, long startClockTicks) {
+ this.pid = pid;
+ this.name = name;
+ this.startTimeMillis = startClockTicks != INVALID_START_TIME
+ ? startClockTicks * MILLIS_PER_JIFFY : INVALID_START_TIME;
+ }
+
+ boolean doMatch(int pid, long startTimeMillis) {
+ // Start time reported by the services that monitor the process health will be either
+ // the actual start time of the pid or the elapsed real time when the pid was last seen
+ // alive. Thus, verify whether the given start time is at least the actual start time of
+ // the pid.
+ return this.pid == pid && (this.startTimeMillis == INVALID_START_TIME
+ || this.startTimeMillis <= startTimeMillis);
+ }
- ICarSystemServerClient carService = ICarSystemServerClient.Stub.asInterface(binder);
- mCarServiceProxy.handleCarServiceConnection(carService);
+ @Override
+ public String toString() {
+ return new StringBuilder("ProcessInfo { pid = ").append(pid)
+ .append(", name = ").append(name)
+ .append(", startTimeMillis = ")
+ .append(startTimeMillis != INVALID_START_TIME ? startTimeMillis : "invalid")
+ .append(" }").toString();
}
}
}
diff --git a/builtInServices/src/com/android/internal/car/CarServiceHelperServiceUpdatable.java b/builtInServices/src/com/android/internal/car/CarServiceHelperServiceUpdatable.java
new file mode 100644
index 0000000..175da48
--- /dev/null
+++ b/builtInServices/src/com/android/internal/car/CarServiceHelperServiceUpdatable.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.car;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.server.wm.CarLaunchParamsModifierUpdatable;
+
+import java.io.PrintWriter;
+import java.util.function.BiConsumer;
+
+/**
+ * Contains calls from CarServiceHelperService (which is a built-in class) to
+ * CarServiceHelperServiceUpdatableImpl (which is a updatable class as part of car-module).
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public interface CarServiceHelperServiceUpdatable {
+
+ void onUserRemoved(@NonNull UserHandle userHandle);
+
+ void onStart();
+
+ void dump(@NonNull PrintWriter pw, @Nullable String[] args);
+
+ void sendUserLifecycleEvent(int eventType, @Nullable UserHandle userFrom,
+ @NonNull UserHandle userTo);
+
+ void onFactoryReset(@NonNull BiConsumer<Integer, Bundle> processFactoryReset);
+
+ void initBootUser();
+
+ CarLaunchParamsModifierUpdatable getCarLaunchParamsModifierUpdatable();
+}
diff --git a/builtInServices/src/com/android/server/wm/ActivityOptionsWrapper.java b/builtInServices/src/com/android/server/wm/ActivityOptionsWrapper.java
new file mode 100644
index 0000000..f9dc751
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/ActivityOptionsWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.SystemApi;
+import android.app.ActivityOptions;
+import android.window.WindowContainerToken;
+
+/**
+ * Wrapper of {@link ActivityOptions}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ActivityOptionsWrapper {
+ private final ActivityOptions mOptions;
+
+ private ActivityOptionsWrapper(ActivityOptions options) {
+ mOptions = options;
+ }
+
+ /** @hide */
+ public static ActivityOptionsWrapper create(ActivityOptions options) {
+ if (options == null) return null;
+ return new ActivityOptionsWrapper(options);
+ }
+
+ /**
+ * Gets the underlying {@link ActivityOptions} that is wrapped by this instance.
+ */
+ // Exposed the original object in order to allow to use the public accessors.
+ public ActivityOptions getOptions() {
+ return mOptions;
+ }
+
+ /**
+ * Gets {@link TaskDisplayAreaWrapper} to launch the Activity into
+ */
+ public TaskDisplayAreaWrapper getLaunchTaskDisplayArea() {
+ WindowContainerToken daToken = mOptions.getLaunchTaskDisplayArea();
+ if (daToken == null) return null;
+ TaskDisplayArea tda = (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder());
+ return TaskDisplayAreaWrapper.create(tda);
+ }
+
+ @Override
+ public String toString() {
+ return mOptions.toString();
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/ActivityRecordWrapper.java b/builtInServices/src/com/android/server/wm/ActivityRecordWrapper.java
new file mode 100644
index 0000000..bd15a54
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/ActivityRecordWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+
+/**
+ * Wrapper of {@link ActivityRecord}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ActivityRecordWrapper {
+ private final ActivityRecord mActivityRecord;
+
+ private ActivityRecordWrapper(ActivityRecord activityRecord) {
+ mActivityRecord = activityRecord;
+ }
+
+ /** @hide */
+ public static ActivityRecordWrapper create(@Nullable ActivityRecord activityRecord) {
+ if (activityRecord == null) return null;
+ return new ActivityRecordWrapper(activityRecord);
+ }
+
+ /** @hide */
+ public ActivityRecord getActivityRecord() {
+ return mActivityRecord;
+ }
+
+ /**
+ * Gets which user this Activity is running for.
+ */
+ public int getUserId() {
+ return mActivityRecord.mUserId;
+ }
+
+ /**
+ * Gets the actual {@link ComponentName} of this Activity.
+ */
+ public ComponentName getComponentName() {
+ if (mActivityRecord.info == null) return null;
+ return mActivityRecord.info.getComponentName();
+ }
+
+ /**
+ * Gets the {@link TaskDisplayAreaWrapper} where this is located.
+ */
+ public TaskDisplayAreaWrapper getDisplayArea() {
+ return TaskDisplayAreaWrapper.create(mActivityRecord.getDisplayArea());
+ }
+
+ /**
+ * Returns whether this Activity is not displayed.
+ */
+ public boolean isNoDisplay() {
+ return mActivityRecord.noDisplay;
+ }
+
+ /**
+ * Gets {@link TaskDisplayAreaWrapper} where the handover Activity is supposed to launch
+ */
+ public TaskDisplayAreaWrapper getHandoverTaskDisplayArea() {
+ return TaskDisplayAreaWrapper.create(mActivityRecord.mHandoverTaskDisplayArea);
+ }
+
+ /**
+ * Gets {@code displayId} where the handover Activity is supposed to launch
+ */
+ public int getHandoverLaunchDisplayId() {
+ return mActivityRecord.mHandoverLaunchDisplayId;
+ }
+
+ /**
+ * Returns whether this Activity allows to be embedded in the other Activity.
+ */
+ public boolean allowingEmbedded() {
+ if (mActivityRecord.info == null) return false;
+ return (mActivityRecord.info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+ }
+
+ /**
+ * Returns whether the display where this Activity is located is trusted.
+ */
+ public boolean isDisplayTrusted() {
+ return mActivityRecord.getDisplayContent().isTrusted();
+ }
+
+ @Override
+ public String toString() {
+ return mActivityRecord.toString();
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/CalculateParams.java b/builtInServices/src/com/android/server/wm/CalculateParams.java
new file mode 100644
index 0000000..8b4faff
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/CalculateParams.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.SystemApi;
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.view.WindowLayout;
+
+/**
+ * Wrapper of the parameters of {@code LaunchParamsController.LaunchParamsModifier.onCalculate()}
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class CalculateParams {
+ private TaskWrapper mTask;
+ private WindowLayoutWrapper mLayout;
+ private ActivityRecordWrapper mActivity;
+ private ActivityRecordWrapper mSource;
+ private ActivityOptionsWrapper mOptions;
+ private RequestWrapper mRequest;
+ private int mPhase;
+ private LaunchParamsWrapper mCurrentParams;
+ private LaunchParamsWrapper mOutParams;
+ private boolean mSupportsMultiDisplay;
+
+ private CalculateParams() {}
+
+ /** @hide */
+ public static CalculateParams create(Task task, ActivityInfo.WindowLayout layout,
+ ActivityRecord actvity, ActivityRecord source,
+ ActivityOptions options, ActivityStarter.Request request, int phase,
+ LaunchParamsController.LaunchParams currentParams,
+ LaunchParamsController.LaunchParams outParms,
+ boolean supportsMultiDisplay) {
+ CalculateParams params = new CalculateParams();
+ params.mTask = TaskWrapper.create(task);
+ params.mLayout = WindowLayoutWrapper.create(layout);
+ params.mActivity = ActivityRecordWrapper.create(actvity);
+ params.mSource = ActivityRecordWrapper.create(source);
+ params.mOptions = ActivityOptionsWrapper.create(options);
+ params.mRequest = RequestWrapper.create(request);
+ params.mPhase = phase;
+ params.mCurrentParams = LaunchParamsWrapper.create(currentParams);
+ params.mOutParams = LaunchParamsWrapper.create(outParms);
+ params.mSupportsMultiDisplay = supportsMultiDisplay;
+ return params;
+ }
+
+ /**
+ * Gets the {@link TaskWrapper} currently being positioned.
+ */
+ public TaskWrapper getTask() {
+ return mTask;
+ }
+
+ /**
+ * Gets the specified {@link WindowLayoutWrapper}.
+ */
+ public WindowLayoutWrapper getWindowLayout() {
+ return mLayout;
+ }
+
+ /**
+ * Gets the {@link ActivityRecordWrapper} currently being positioned.
+ */
+ public ActivityRecordWrapper getActivity() {
+ return mActivity;
+ }
+
+ /**
+ * Gets the {@link ActivityRecordWrapper} from which activity was started from.
+ */
+ public ActivityRecordWrapper getSource() {
+ return mSource;
+ }
+
+ /**
+ * Gets the {@link ActivityOptionsWrapper} specified for the activity.
+ */
+ public ActivityOptionsWrapper getOptions() {
+ return mOptions;
+ }
+
+ /**
+ * Gets the optional {@link RequestWrapper} from the activity starter.
+ */
+ public RequestWrapper getRequest() {
+ return mRequest;
+ }
+
+ /**
+ * Gets the {@link LaunchParamsController.LaunchParamsModifier.Phase} that the resolution should
+ * finish.
+ */
+ public int getPhase() {
+ return mPhase;
+ }
+
+ /**
+ * Gets the current {@link LaunchParamsWrapper}.
+ */
+ public LaunchParamsWrapper getCurrentParams() {
+ return mCurrentParams;
+ }
+
+ /**
+ * Gets the resulting {@link LaunchParamsWrapper}.
+ */
+ public LaunchParamsWrapper getOutParams() {
+ return mOutParams;
+ }
+
+ /**
+ * Returns whether the current system supports the multiple display.
+ */
+ public boolean supportsMultiDisplay() {
+ return mSupportsMultiDisplay;
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/CarDisplayAreaPolicyProvider.java b/builtInServices/src/com/android/server/wm/CarDisplayAreaPolicyProvider.java
new file mode 100644
index 0000000..ea4a4bf
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/CarDisplayAreaPolicyProvider.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provider for platform-default car display area policy for reference design.
+ *
+ * @hide
+ */
+public class CarDisplayAreaPolicyProvider implements DisplayAreaPolicy.Provider {
+
+ /**
+ * This display area is mandatory to be defined. This is where the applications will be
+ * launched.
+ */
+ private static final int DEFAULT_APP_TASK_CONTAINER = FEATURE_DEFAULT_TASK_CONTAINER;
+
+ /**
+ * The display partition to launch applications by default. This contains {@link
+ * #DEFAULT_APP_TASK_CONTAINER}.
+ */
+ private static final int FOREGROUND_DISPLAY_AREA_ROOT = FEATURE_VENDOR_FIRST + 1;
+
+ /**
+ * Background applications task container.
+ */
+ private static final int BACKGROUND_TASK_CONTAINER = FEATURE_VENDOR_FIRST + 2;
+ private static final int FEATURE_TASKDISPLAYAREA_PARENT = FEATURE_VENDOR_FIRST + 3;
+
+ /**
+ * Control bar task container.
+ *
+ * Currently we are launching CarLauncher activity in this TDA. This is because the audio card
+ * implementation today is using fragments. If that changes in future then we can use the window
+ * instead to display that view instead of fragments that need an activity.
+ */
+ private static final int CONTROL_BAR_DISPLAY_AREA = FEATURE_VENDOR_FIRST + 4;
+
+ /**
+ * Feature to display the title bar.
+ */
+ private static final int FEATURE_TITLE_BAR = FEATURE_VENDOR_FIRST + 5;
+
+ /**
+ * Feature to display voice plate.
+ */
+ private static final int FEATURE_VOICE_PLATE = FEATURE_VENDOR_FIRST + 6;
+
+ @Override
+ public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
+ RootDisplayArea root, DisplayArea.Tokens imeContainer) {
+
+ if (!content.isDefaultDisplay) {
+ return new DisplayAreaPolicy.DefaultProvider().instantiate(wmService, content, root,
+ imeContainer);
+ }
+
+ TaskDisplayArea backgroundTaskDisplayArea = new TaskDisplayArea(content, wmService,
+ "BackgroundTaskDisplayArea", BACKGROUND_TASK_CONTAINER,
+ /* createdByOrganizer= */ false, /* canHostHomeTask= */ false);
+
+ TaskDisplayArea controlBarDisplayArea = new TaskDisplayArea(content, wmService,
+ "ControlBarTaskDisplayArea", CONTROL_BAR_DISPLAY_AREA,
+ /* createdByOrganizer= */ false, /* canHostHomeTask= */ false);
+
+ TaskDisplayArea voicePlateTaskDisplayArea = new TaskDisplayArea(content, wmService,
+ "VoicePlateTaskDisplayArea", FEATURE_VOICE_PLATE,
+ /* createdByOrganizer= */ false, /* canHostHomeTask= */ false);
+
+ List<TaskDisplayArea> backgroundTdaList = new ArrayList<>();
+ backgroundTdaList.add(voicePlateTaskDisplayArea);
+ backgroundTdaList.add(backgroundTaskDisplayArea);
+ backgroundTdaList.add(controlBarDisplayArea);
+
+ // Root
+ DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ .setTaskDisplayAreas(backgroundTdaList)
+ .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
+ "ImePlaceholder", FEATURE_IME_PLACEHOLDER)
+ .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
+ .build())
+ // to make sure there are 2 children under root.
+ // TODO: replace when b/188102153 is resolved to set this to top.
+ .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
+ "TaskDisplayAreaParent", FEATURE_TASKDISPLAYAREA_PARENT)
+ .and(TYPE_APPLICATION)
+ .build());
+
+ // Default application launches here
+ RootDisplayArea defaultAppsRoot = new DisplayAreaGroup(wmService,
+ "FeatureForegroundApplication", FOREGROUND_DISPLAY_AREA_ROOT);
+ TaskDisplayArea defaultAppTaskDisplayArea = new TaskDisplayArea(content, wmService,
+ "DefaultApplicationTaskDisplayArea", DEFAULT_APP_TASK_CONTAINER);
+ List<TaskDisplayArea> firstTdaList = new ArrayList<>();
+ firstTdaList.add(defaultAppTaskDisplayArea);
+ DisplayAreaPolicyBuilder.HierarchyBuilder applicationHierarchy =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(defaultAppsRoot)
+ .setTaskDisplayAreas(firstTdaList)
+ .setImeContainer(imeContainer)
+ .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
+ "ImePlaceholder", FEATURE_IME_PLACEHOLDER)
+ .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
+ .build())
+ .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
+ "TitleBar", FEATURE_TITLE_BAR)
+ .and(TYPE_APPLICATION_OVERLAY)
+ .build());
+
+ return new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(rootHierarchy)
+ .addDisplayAreaGroupHierarchy(applicationHierarchy)
+ .build(wmService);
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/CarLaunchParamsModifier.java b/builtInServices/src/com/android/server/wm/CarLaunchParamsModifier.java
new file mode 100644
index 0000000..6472ffb
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/CarLaunchParamsModifier.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.ActivityStarter.Request;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Display;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to control the assignment of a display for Car while launching a Activity.
+ *
+ * <p>This one controls which displays users are allowed to launch.
+ * The policy should be passed from car service through
+ * {@link com.android.internal.car.ICarServiceHelper} binder interfaces. If no policy is set,
+ * this module will not change anything for launch process.</p>
+ *
+ * <p> The policy can only affect which display passenger users can use. Current user, assumed
+ * to be a driver user, is allowed to launch any display always.</p>
+ *
+ * @hide
+ */
+public final class CarLaunchParamsModifier implements LaunchParamsController.LaunchParamsModifier {
+
+ private final Context mContext;
+
+ private DisplayManager mDisplayManager; // set only from init()
+ private ActivityTaskManagerService mAtm; // set only from init()
+
+ private CarLaunchParamsModifierUpdatable mUpdatable;
+
+ // getFallbackDisplayAreasForActivity() can return the most 3 {@link TaskDisplayAreaWrapper}.
+ private final ArrayList<TaskDisplayAreaWrapper> mFallBackDisplayAreaList = new ArrayList<>(3);
+
+ /** Constructor. Can be constructed any time. */
+ public CarLaunchParamsModifier(Context context) {
+ // This can be very early stage. So postpone interaction with other system until init.
+ mContext = context;
+ }
+
+ public void setUpdatable(CarLaunchParamsModifierUpdatable updatable) {
+ mUpdatable = updatable;
+ }
+
+ public CarLaunchParamsModifierInterface getBuiltinInterface() {
+ return mBuiltinInterface;
+ }
+
+ /**
+ * Initializes all internal stuffs. This should be called only after ATMS, DisplayManagerService
+ * are ready.
+ */
+ public void init() {
+ mAtm = (ActivityTaskManagerService) ActivityTaskManager.getService();
+ LaunchParamsController controller = mAtm.mTaskSupervisor.getLaunchParamsController();
+ controller.registerModifier(this);
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mUpdatable.getDisplayListener(),
+ new Handler(Looper.getMainLooper()));
+ }
+
+ /** Notifies user switching. */
+ public void handleUserStarting(@UserIdInt int startingUserId) {
+ mUpdatable.handleUserStarting(startingUserId);
+ }
+
+ /** Notifies user switching. */
+ public void handleCurrentUserSwitching(@UserIdInt int newUserId) {
+ mUpdatable.handleCurrentUserSwitching(newUserId);
+ }
+
+ /** Notifies user stopped. */
+ public void handleUserStopped(@UserIdInt int stoppedUser) {
+ mUpdatable.handleUserStopped(stoppedUser);
+ }
+
+ /**
+ * Decides display to assign while an Activity is launched.
+ *
+ * <p>For current user (=driver), launching to any display is allowed as long as system
+ * allows it.</p>
+ *
+ * <p>For private display, do not change anything as private display has its own logic.</p>
+ *
+ * <p>For passenger displays, only run in allowed displays. If requested display is not
+ * allowed, change to the 1st allowed display.</p>
+ */
+ @Override
+ @Result
+ public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
+ @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
+ ActivityOptions options, @Nullable Request request, int phase,
+ LaunchParamsController.LaunchParams currentParams,
+ LaunchParamsController.LaunchParams outParams) {
+ CalculateParams params = CalculateParams.create(task, layout, activity, source,
+ options, request, phase, currentParams, outParams, mAtm.mSupportsMultiDisplay);
+ return mUpdatable.calculate(params);
+ }
+
+ @Nullable
+ private TaskDisplayAreaWrapper getDefaultTaskDisplayAreaOnDisplay(int displayId) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ return null;
+ }
+ DisplayContent dc = mAtm.mRootWindowContainer.getDisplayContentOrCreate(displayId);
+ if (dc == null) {
+ return null;
+ }
+ return TaskDisplayAreaWrapper.create(dc.getDefaultTaskDisplayArea());
+ }
+
+ /**
+ * Calculates the default {@link TaskDisplayAreaWrapper} for a task. We attempt to put
+ * the activity within the same display area if possible. The strategy is to find the display
+ * in the following order:
+ *
+ * <ol>
+ * <li>The display area of the top activity from the launching process will be used</li>
+ * <li>The display area of the top activity from the real launching process will be used
+ * </li>
+ * <li>Default display area from the associated root window container.</li>
+ * </ol>
+ * @param activityRecordWrapper the activity being started
+ * @param requestWrapper optional {@link RequestWrapper} made to start the activity record
+ * @return the list of {@link TaskDisplayAreaWrapper} to house the task
+ */
+ private List<TaskDisplayAreaWrapper> getFallbackDisplayAreasForActivity(
+ @NonNull ActivityRecordWrapper activityRecordWrapper,
+ @Nullable RequestWrapper requestWrapper) {
+ ActivityRecord activityRecord = activityRecordWrapper.getActivityRecord();
+ Request request = requestWrapper != null ? requestWrapper.getRequest() : null;
+ mFallBackDisplayAreaList.clear();
+
+ WindowProcessController controllerFromLaunchingRecord = mAtm.getProcessController(
+ activityRecord.launchedFromPid, activityRecord.launchedFromUid);
+ TaskDisplayArea displayAreaForLaunchingRecord = controllerFromLaunchingRecord == null
+ ? null : controllerFromLaunchingRecord.getTopActivityDisplayArea();
+ if (displayAreaForLaunchingRecord != null) {
+ mFallBackDisplayAreaList.add(
+ TaskDisplayAreaWrapper.create(displayAreaForLaunchingRecord));
+ }
+
+ WindowProcessController controllerFromProcess = mAtm.getProcessController(
+ activityRecord.getProcessName(), activityRecord.getUid());
+ TaskDisplayArea displayAreaForRecord = controllerFromProcess == null ? null
+ : controllerFromProcess.getTopActivityDisplayArea();
+ if (displayAreaForRecord != null) {
+ mFallBackDisplayAreaList.add(TaskDisplayAreaWrapper.create(displayAreaForRecord));
+ }
+
+ WindowProcessController controllerFromRequest =
+ request == null ? null : mAtm.getProcessController(request.realCallingPid,
+ request.realCallingUid);
+ TaskDisplayArea displayAreaFromSourceProcess = controllerFromRequest == null ? null
+ : controllerFromRequest.getTopActivityDisplayArea();
+ if (displayAreaFromSourceProcess != null) {
+ mFallBackDisplayAreaList.add(
+ TaskDisplayAreaWrapper.create(displayAreaFromSourceProcess));
+ }
+ return mFallBackDisplayAreaList;
+ }
+
+ @Nullable
+ private TaskDisplayAreaWrapper findTaskDisplayArea(int displayId, int featureId) {
+ DisplayContent display = mAtm.mRootWindowContainer.getDisplayContentOrCreate(displayId);
+ if (display == null) {
+ return null;
+ }
+ TaskDisplayArea tda = display.getItemFromTaskDisplayAreas(
+ displayArea -> displayArea.mFeatureId == featureId ? displayArea : null);
+ return TaskDisplayAreaWrapper.create(tda);
+ }
+
+ private final CarLaunchParamsModifierInterface mBuiltinInterface
+ = new CarLaunchParamsModifierInterface() {
+ @Nullable
+ @Override
+ public TaskDisplayAreaWrapper findTaskDisplayArea(int displayId, int featureId) {
+ return CarLaunchParamsModifier.this.findTaskDisplayArea(displayId, featureId);
+ }
+
+ @Nullable
+ @Override
+ public TaskDisplayAreaWrapper getDefaultTaskDisplayAreaOnDisplay(int displayId) {
+ return CarLaunchParamsModifier.this.getDefaultTaskDisplayAreaOnDisplay(displayId);
+ }
+
+ @NonNull
+ @Override
+ public List<TaskDisplayAreaWrapper> getFallbackDisplayAreasForActivity(
+ @NonNull ActivityRecordWrapper activityRecord, @Nullable RequestWrapper request) {
+ return CarLaunchParamsModifier.this.getFallbackDisplayAreasForActivity(
+ activityRecord, request);
+ }
+ };
+}
diff --git a/builtInServices/src/com/android/server/wm/CarLaunchParamsModifierInterface.java b/builtInServices/src/com/android/server/wm/CarLaunchParamsModifierInterface.java
new file mode 100644
index 0000000..8550c55
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/CarLaunchParamsModifierInterface.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.view.Display;
+
+import java.util.List;
+
+/**
+ * Interface implemented by {@code CarLaunchParamsModifier} and used by
+ * {@code CarLaunchParamsModifierUpdatable}.
+ *
+ * Because {@code CarLaunchParamsModifierUpdatable} calls {@code CarLaunchParamsModifierInterface}
+ * with {@code mLock} acquired, {@code CarLaunchParamsModifierInterface} shouldn't call
+ * {@code CarLaunchParamsModifierUpdatable} again during its execution.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public interface CarLaunchParamsModifierInterface {
+ /**
+ * Returns {@link TaskDisplayAreaWrapper} of the given {@code featureId} in the given
+ * {@code displayId}.
+ */
+ @Nullable TaskDisplayAreaWrapper findTaskDisplayArea(int displayId, int featureId);
+
+ /**
+ * Returns the default {@link TaskDisplayAreaWrapper} of the given {@code displayId}.
+ */
+ @Nullable TaskDisplayAreaWrapper getDefaultTaskDisplayAreaOnDisplay(int displayId);
+
+ /**
+ * Returns the list of fallback {@link TaskDisplayAreaWrapper} from the source of the request.
+ */
+ @NonNull List<TaskDisplayAreaWrapper> getFallbackDisplayAreasForActivity(
+ @NonNull ActivityRecordWrapper activityRecord, @Nullable RequestWrapper request);
+
+}
diff --git a/builtInServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatable.java b/builtInServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatable.java
new file mode 100644
index 0000000..3abf54e
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatable.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
+import android.car.app.CarActivityManager;
+import android.content.ComponentName;
+import android.hardware.display.DisplayManager;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.view.Display;
+import android.window.DisplayAreaOrganizer;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Updatable interface of CarLaunchParamsModifier.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public interface CarLaunchParamsModifierUpdatable {
+
+ /** Returns {@link DisplayManager.DisplayListener} of CarLaunchParamsModifierUpdatable. */
+ DisplayManager.DisplayListener getDisplayListener();
+
+ /** Notifies user switching. */
+ void handleCurrentUserSwitching(@UserIdInt int newUserId);
+
+ /** Notifies user starting. */
+ void handleUserStarting(@UserIdInt int startingUser);
+
+ /** Notifies user stopped. */
+ void handleUserStopped(@UserIdInt int stoppedUser);
+
+ /**
+ * Calculates {@code outParams} based on the given arguments.
+ * See {@code LaunchParamsController.LaunchParamsModifier.onCalculate()} for the detail.
+ */
+ int calculate(CalculateParams params);
+}
diff --git a/builtInServices/src/com/android/server/wm/LaunchParamsWrapper.java b/builtInServices/src/com/android/server/wm/LaunchParamsWrapper.java
new file mode 100644
index 0000000..344961f
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/LaunchParamsWrapper.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+
+/**
+ * Wrapper of {@link com.android.server.wm.LaunchParamsController.LaunchParams}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class LaunchParamsWrapper {
+ /** Returned when the modifier does not want to influence the bounds calculation */
+ public static int RESULT_SKIP = LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+ /**
+ * Returned when the modifier has changed the bounds and would like its results to be the
+ * final bounds applied.
+ */
+ public static int RESULT_DONE = LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+ /**
+ * Returned when the modifier has changed the bounds but is okay with other modifiers
+ * influencing the bounds.
+ */
+ public static int RESULT_CONTINUE = LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+
+ private final LaunchParamsController.LaunchParams mLaunchParams;
+
+ private LaunchParamsWrapper(LaunchParamsController.LaunchParams launchParams) {
+ mLaunchParams = launchParams;
+ }
+
+ /** @hide */
+ public static LaunchParamsWrapper create(
+ @Nullable LaunchParamsController.LaunchParams launchParams) {
+ if (launchParams == null) return null;
+ return new LaunchParamsWrapper(launchParams);
+ }
+
+ /**
+ * Gets the {@link TaskDisplayAreaWrapper} the {@link Task} would prefer to be on.
+ */
+ public TaskDisplayAreaWrapper getPreferredTaskDisplayArea() {
+ return TaskDisplayAreaWrapper.create(mLaunchParams.mPreferredTaskDisplayArea);
+ }
+
+ /**
+ * Sets the {@link TaskDisplayAreaWrapper} the {@link Task} would prefer to be on.
+ */
+ public void setPreferredTaskDisplayArea(TaskDisplayAreaWrapper tda) {
+ mLaunchParams.mPreferredTaskDisplayArea = tda.getTaskDisplayArea();
+ }
+
+ /**
+ * Gets the windowing mode to be in.
+ */
+ public int getWindowingMode() {
+ return mLaunchParams.mWindowingMode;
+ }
+
+ /**
+ * Sets the windowing mode to be in.
+ */
+ public void setWindowingMode(int windowingMode) {
+ mLaunchParams.mWindowingMode = windowingMode;
+ }
+
+ /**
+ * Gets the bounds within the parent container.
+ */
+ public Rect getBounds() {
+ return mLaunchParams.mBounds;
+ }
+
+ /**
+ * Sets the bounds within the parent container.
+ */
+ public void setBounds(Rect bounds) {
+ mLaunchParams.mBounds.set(bounds);
+ }
+
+ @Override
+ public String toString() {
+ return "LaunchParams{" +
+ "mPreferredTaskDisplayArea=" + mLaunchParams.mPreferredTaskDisplayArea +
+ ", mWindowingMode=" + mLaunchParams.mWindowingMode +
+ ", mBounds=" + mLaunchParams.mBounds.toString() + '}';
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/RequestWrapper.java b/builtInServices/src/com/android/server/wm/RequestWrapper.java
new file mode 100644
index 0000000..d6e1795
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/RequestWrapper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Wrapper of {@link com.android.server.wm.ActivityStarter.Request}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class RequestWrapper {
+ private final ActivityStarter.Request mRequest;
+
+ private RequestWrapper(ActivityStarter.Request request) {
+ mRequest = request;
+ }
+
+ /** @hide */
+ public static RequestWrapper create(@Nullable ActivityStarter.Request request) {
+ if (request == null) return null;
+ return new RequestWrapper(request);
+ }
+
+ /** @hide */
+ public ActivityStarter.Request getRequest() {
+ return mRequest;
+ }
+
+ @Override
+ public String toString() {
+ return mRequest.toString();
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/TaskDisplayAreaWrapper.java b/builtInServices/src/com/android/server/wm/TaskDisplayAreaWrapper.java
new file mode 100644
index 0000000..9faef06
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/TaskDisplayAreaWrapper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.view.Display;
+
+/**
+ * Wrapper of {@link TaskDisplayArea}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TaskDisplayAreaWrapper {
+ private final TaskDisplayArea mTaskDisplayArea;
+
+ private TaskDisplayAreaWrapper(TaskDisplayArea taskDisplayArea) {
+ mTaskDisplayArea = taskDisplayArea;
+ }
+
+ /** @hide */
+ public static TaskDisplayAreaWrapper create(@Nullable TaskDisplayArea taskDisplayArea) {
+ if (taskDisplayArea == null) return null;
+ return new TaskDisplayAreaWrapper(taskDisplayArea);
+ }
+
+ /** @hide */
+ public TaskDisplayArea getTaskDisplayArea() {
+ return mTaskDisplayArea;
+ }
+
+ /**
+ * Gets the display this {@link TaskDisplayAreaWrapper} is on.
+ */
+ public Display getDisplay() {
+ return mTaskDisplayArea.getDisplayContent().getDisplay();
+ }
+
+ @Override
+ public String toString() {
+ return mTaskDisplayArea.toString();
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/TaskWrapper.java b/builtInServices/src/com/android/server/wm/TaskWrapper.java
new file mode 100644
index 0000000..3a2c682
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/TaskWrapper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Wrapper of {@link Task}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TaskWrapper {
+ private final Task mTask;
+
+ private TaskWrapper(Task task) {
+ mTask = task;
+ }
+
+ /** @hide */
+ public static TaskWrapper create(@Nullable Task task) {
+ if (task == null) return null;
+ return new TaskWrapper(task);
+ }
+
+ /**
+ * Gets the {@code userId} of this {@link Task} is created for
+ */
+ public int getUserId() {
+ return mTask.mUserId;
+ }
+
+ /**
+ * Gets the root {@link TaskWrapper} of the this.
+ */
+ public TaskWrapper getRootTask() {
+ return create(mTask.getRootTask());
+ }
+
+ /**
+ * Gets the {@link TaskDisplayAreaWrapper} this {@link Task} is on.
+ */
+ public TaskDisplayAreaWrapper getTaskDisplayArea() {
+ return TaskDisplayAreaWrapper.create(mTask.getTaskDisplayArea());
+ }
+
+ @Override
+ public String toString() {
+ return mTask.toString();
+ }
+}
diff --git a/builtInServices/src/com/android/server/wm/WindowLayoutWrapper.java b/builtInServices/src/com/android/server/wm/WindowLayoutWrapper.java
new file mode 100644
index 0000000..e028a20
--- /dev/null
+++ b/builtInServices/src/com/android/server/wm/WindowLayoutWrapper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.pm.ActivityInfo;
+
+/**
+ * Wrapper of {@link android.content.pm.ActivityInfo.WindowLayout}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class WindowLayoutWrapper {
+ private final ActivityInfo.WindowLayout mLayout;
+
+ private WindowLayoutWrapper(ActivityInfo.WindowLayout layout) {
+ mLayout = layout;
+ }
+
+ /** @hide */
+ public static WindowLayoutWrapper create(@Nullable ActivityInfo.WindowLayout layout) {
+ if (layout == null) return null;
+ return new WindowLayoutWrapper(layout);
+ }
+
+ @Override
+ public String toString() {
+ return mLayout.toString();
+ }
+}
diff --git a/tests/Android.mk b/builtInServices/tests/Android.mk
index 1cf4dcc..c9eee58 100644
--- a/tests/Android.mk
+++ b/builtInServices/tests/Android.mk
@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-java-files-under, ../src) \
$(call all-Iaidl-files-under, ../src)
-LOCAL_PACKAGE_NAME := CarServicesTest
+LOCAL_PACKAGE_NAME := FrameworkOptCarServicesTest
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_PRIVATE_PLATFORM_APIS := true
@@ -23,21 +23,21 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_JAVA_LIBRARIES += \
+ android.car \
android.test.runner \
android.test.base \
android.hardware.automotive.vehicle-V2.0-java \
- com.android.car.internal.common
LOCAL_STATIC_JAVA_LIBRARIES := \
android.car.test.utils \
android.car.watchdoglib \
androidx.test.ext.junit \
androidx.test.rules \
- com.android.car.internal.system \
mockito-target-extended-minus-junit4 \
services.core \
testng \
- truth-prebuilt
+ truth-prebuilt \
+ android.car.builtin \
# mockito-target-extended dependencies
LOCAL_JNI_SHARED_LIBRARIES := \
diff --git a/tests/AndroidManifest.xml b/builtInServices/tests/AndroidManifest.xml
index ff050df..edce0fd 100644
--- a/tests/AndroidManifest.xml
+++ b/builtInServices/tests/AndroidManifest.xml
@@ -22,7 +22,7 @@
android:targetPackage="com.android.internal.car"
android:label="Unit Tests for Car Framework Services"/>
- <application android:label="CarServicesTest"
+ <application android:label="FrameworkOptCarServicesTest"
android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/src/com/android/internal/car/CarDevicePolicySafetyCheckerTest.java b/builtInServices/tests/src/com/android/internal/car/CarDevicePolicySafetyCheckerTest.java
index ef50f92..ef50f92 100644
--- a/tests/src/com/android/internal/car/CarDevicePolicySafetyCheckerTest.java
+++ b/builtInServices/tests/src/com/android/internal/car/CarDevicePolicySafetyCheckerTest.java
diff --git a/tests/src/com/android/internal/car/CarServiceHelperServiceTest.java b/builtInServices/tests/src/com/android/internal/car/CarServiceHelperServiceTest.java
index a2da2a5..552a27a 100644
--- a/tests/src/com/android/internal/car/CarServiceHelperServiceTest.java
+++ b/builtInServices/tests/src/com/android/internal/car/CarServiceHelperServiceTest.java
@@ -16,23 +16,19 @@
package com.android.internal.car;
-import static com.android.car.internal.common.CommonConstants.CAR_SERVICE_INTERFACE;
+import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STARTING;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPING;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.server.SystemService.UserCompletedEventType.newUserCompletedEventTypeForTest;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -40,18 +36,16 @@ import android.annotation.UserIdInt;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.car.watchdoglib.CarWatchdogDaemonHelper;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-
+import android.os.SystemProperties.Handle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
+import com.android.server.SystemService.UserCompletedEventType;
import com.android.server.wm.CarLaunchParamsModifier;
import org.junit.Before;
@@ -65,9 +59,6 @@ import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase {
- private static final String TAG = CarServiceHelperServiceTest.class.getSimpleName();
-
- private CarServiceHelperService mHelperSpy;
private CarServiceHelperService mHelper;
@Mock
@@ -81,7 +72,14 @@ public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase
@Mock
private IBinder mICarBinder;
@Mock
- private CarServiceProxy mCarServiceProxy;
+ private CarServiceHelperServiceUpdatable mCarServiceHelperServiceUpdatable;
+
+ @Mock
+ private CarDevicePolicySafetyChecker mCarDevicePolicySafetyChecker;
+
+ public CarServiceHelperServiceTest() {
+ super(CarServiceHelperService.TAG);
+ }
/**
* Initialize objects and setup testing environment.
@@ -97,40 +95,18 @@ public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase
mMockContext,
mCarLaunchParamsModifier,
mCarWatchdogDaemonHelper,
- mCarServiceProxy);
- mHelperSpy = spy(mHelper);
+ mCarServiceHelperServiceUpdatable,
+ mCarDevicePolicySafetyChecker);
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
}
@Test
- public void testCarServiceLaunched() throws Exception {
- mockRegisterReceiver();
- mockBindService();
- mockLoadLibrary();
-
- mHelperSpy.onStart();
-
- verifyBindService();
- }
-
- @Test
- public void testHandleCarServiceCrash() throws Exception {
- mockHandleCarServiceCrash();
- mockCarServiceException();
-
- mHelperSpy.handleCarServiceConnection(mICarBinder);
-
- verify(mHelperSpy).handleCarServiceCrash();
- }
-
- @Test
public void testOnUserStarting_notifiesICar() throws Exception {
int userId = 10;
mHelper.onUserStarting(newTargetUser(userId));
- verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_STARTING,
- UserHandle.USER_NULL, userId);
+ verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_STARTING, userId);
}
@Test
@@ -165,8 +141,7 @@ public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase
mHelper.onUserUnlocking(newTargetUser(userId));
- verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING,
- UserHandle.USER_NULL, userId);
+ verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING, userId);
}
@Test
@@ -182,8 +157,7 @@ public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase
mHelper.onUserStopping(newTargetUser(userId));
- verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_STOPPING,
- UserHandle.USER_NULL, userId);
+ verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_STOPPING, userId);
}
@Test
@@ -199,8 +173,7 @@ public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase
mHelper.onUserStopped(newTargetUser(userId));
- verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_STOPPED,
- UserHandle.USER_NULL, userId);
+ verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_STOPPED, userId);
}
@Test
@@ -217,45 +190,39 @@ public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase
verifyInitBootUser();
}
- private TargetUser newTargetUser(int userId) {
- return newTargetUser(userId, /* preCreated= */ false);
- }
+ @Test
+ public void testOnUserCompletedEvent_notifiesPostUnlockedEvent() throws Exception {
+ int userId = 10;
- private TargetUser newTargetUser(int userId, boolean preCreated) {
- TargetUser targetUser = mock(TargetUser.class);
- when(targetUser.getUserIdentifier()).thenReturn(userId);
- when(targetUser.isPreCreated()).thenReturn(preCreated);
- return targetUser;
- }
+ mHelper.onUserCompletedEvent(newTargetUser(userId), newUserCompletedEventTypeForTest(
+ UserCompletedEventType.EVENT_TYPE_USER_UNLOCKED));
- private void verifyBindService() throws Exception {
- verify(mMockContext).bindServiceAsUser(
- argThat(intent -> intent.getAction().equals(CAR_SERVICE_INTERFACE)),
- any(), eq(Context.BIND_AUTO_CREATE), any(), eq(UserHandle.SYSTEM));
+ verifyICarOnUserLifecycleEventCalled(USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, userId);
}
- private void mockRegisterReceiver() {
- when(mMockContext.registerReceiverForAllUsers(any(), any(), any(), any()))
- .thenReturn(new Intent());
- }
+ @Test
+ public void testOnUserCompletedEvent_preCreatedUserDoesNotNotifyICar() throws Exception {
+ UserCompletedEventType userCompletedEventType = newUserCompletedEventTypeForTest(
+ UserCompletedEventType.EVENT_TYPE_USER_STARTING
+ | UserCompletedEventType.EVENT_TYPE_USER_SWITCHING
+ | UserCompletedEventType.EVENT_TYPE_USER_UNLOCKED);
- private void mockBindService() {
- when(mMockContext.bindServiceAsUser(any(), any(),
- eq(Context.BIND_AUTO_CREATE), any(), eq(UserHandle.SYSTEM)))
- .thenReturn(true);
- }
+ mHelper.onUserCompletedEvent(newTargetUser(10, /* preCreated= */true),
+ userCompletedEventType);
- private void mockLoadLibrary() {
- doNothing().when(mHelperSpy).loadNativeLibrary();
+ verifyICarOnUserLifecycleEventNeverCalled();
}
- private void mockCarServiceException() throws Exception {
- when(mICarBinder.transact(anyInt(), notNull(), isNull(), eq(Binder.FLAG_ONEWAY)))
- .thenThrow(new RemoteException("mock car service Crash"));
+ private TargetUser newTargetUser(int userId) {
+ return newTargetUser(userId, /* preCreated= */ false);
}
- private void mockHandleCarServiceCrash() throws Exception {
- doNothing().when(mHelperSpy).handleCarServiceCrash();
+ private TargetUser newTargetUser(int userId, boolean preCreated) {
+ TargetUser targetUser = mock(TargetUser.class);
+ when(targetUser.getUserIdentifier()).thenReturn(userId);
+ when(targetUser.getUserHandle()).thenReturn(UserHandle.of(userId));
+ when(targetUser.isPreCreated()).thenReturn(preCreated);
+ return targetUser;
}
enum InitialUserInfoAction {
@@ -272,21 +239,22 @@ public class CarServiceHelperServiceTest extends AbstractExtendedMockitoTestCase
private void verifyICarOnUserLifecycleEventCalled(int eventType,
@UserIdInt int fromId, @UserIdInt int toId) throws Exception {
- verify(mCarServiceProxy).sendUserLifecycleEvent(eq(eventType),
- isTargetUser(fromId), isTargetUser(toId));
+ verify(mCarServiceHelperServiceUpdatable).sendUserLifecycleEvent(eventType,
+ UserHandle.of(fromId), UserHandle.of(toId));
}
- private static TargetUser isTargetUser(@UserIdInt int userId) {
- return argThat((user) -> {
- return user == null || user.getUserIdentifier() == userId;
- });
+ private void verifyICarOnUserLifecycleEventCalled(int eventType,
+ @UserIdInt int userId) throws Exception {
+ verify(mCarServiceHelperServiceUpdatable).sendUserLifecycleEvent(eventType,
+ null, UserHandle.of(userId));
}
private void verifyICarOnUserLifecycleEventNeverCalled() throws Exception {
- verify(mCarServiceProxy, never()).sendUserLifecycleEvent(anyInt(), any(), any());
+ verify(mCarServiceHelperServiceUpdatable, never()).sendUserLifecycleEvent(anyInt(), any(),
+ any());
}
private void verifyInitBootUser() throws Exception {
- verify(mCarServiceProxy).initBootUser();
+ verify(mCarServiceHelperServiceUpdatable).initBootUser();
}
}
diff --git a/builtInServices/tests/src/com/android/server/wm/ActivityOptionsWrapperTest.java b/builtInServices/tests/src/com/android/server/wm/ActivityOptionsWrapperTest.java
new file mode 100644
index 0000000..682c31f
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/ActivityOptionsWrapperTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityOptions;
+
+import org.junit.Test;
+
+/**
+ * This class contains unit tests for the {@link ActivityOptionsWrapper}.
+ */
+public final class ActivityOptionsWrapperTest {
+ @Test
+ public void create_returnsActivityOptionWrapper() {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ ActivityOptionsWrapper wrapper = ActivityOptionsWrapper.create(options);
+ assertThat(wrapper).isNotNull();
+ assertThat(wrapper.getOptions()).isSameInstanceAs(options);
+ assertThat(wrapper.toString()).isEqualTo(options.toString());
+ }
+
+ @Test
+ public void create_returnsNull() {
+ ActivityOptionsWrapper wrapper = ActivityOptionsWrapper.create(null);
+ assertThat(wrapper).isNull();
+ }
+}
diff --git a/builtInServices/tests/src/com/android/server/wm/ActivityRecordWrapperTest.java b/builtInServices/tests/src/com/android/server/wm/ActivityRecordWrapperTest.java
new file mode 100644
index 0000000..302f974
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/ActivityRecordWrapperTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * This class contains unit tests for the {@link ActivityRecordWrapper}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class ActivityRecordWrapperTest {
+ @Mock
+ private ActivityRecord mActivityRecord;
+
+ @Test
+ public void create_returnsActivityOptionWrapper() {
+ ActivityRecordWrapper wrapper = ActivityRecordWrapper.create(mActivityRecord);
+ assertThat(wrapper).isNotNull();
+ assertThat(wrapper.getActivityRecord()).isSameInstanceAs(mActivityRecord);
+ assertThat(wrapper.toString()).isEqualTo(mActivityRecord.toString());
+ }
+
+ @Test
+ public void create_returnsNull() {
+ ActivityRecordWrapper wrapper = ActivityRecordWrapper.create(null);
+ assertThat(wrapper).isNull();
+ }
+}
diff --git a/builtInServices/tests/src/com/android/server/wm/CalculateParamsTest.java b/builtInServices/tests/src/com/android/server/wm/CalculateParamsTest.java
new file mode 100644
index 0000000..bf34497
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/CalculateParamsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.view.Gravity;
+
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * This class contains unit tests for the {@link CalculateParams}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class CalculateParamsTest {
+ @Mock
+ private Task mTask;
+ private ActivityInfo.WindowLayout mLayout = new ActivityInfo.WindowLayout(
+ /* width= */ 1280, /* widthFraction= */ 0.5f,
+ /* height= */ 800, /* heightFraction= */ 1.0f,
+ /* gravity= */ Gravity.CENTER, /* minWidth= */ 400, /* minHeight= */ 300);
+ @Mock
+ private ActivityRecord mActvity;
+ @Mock
+ private ActivityRecord mSource;
+ private ActivityOptions mOptions = ActivityOptions.makeBasic();
+ private ActivityStarter.Request mRequest = new ActivityStarter.Request();
+ private int mPhase = LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
+ private LaunchParams mCurrentParams = new LaunchParams();
+ private LaunchParams mOutParms = new LaunchParams();
+ boolean mSupportsMultiDisplay = true;
+
+ @Test
+ public void createReturnsCalculateParams() {
+ mCurrentParams.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ mOutParms.mWindowingMode = WINDOWING_MODE_MULTI_WINDOW;
+ CalculateParams params = CalculateParams.create(mTask,mLayout, mActvity, mSource, mOptions,
+ mRequest, mPhase, mCurrentParams, mOutParms, mSupportsMultiDisplay);
+ // Current toString() of Wrappers are using toString() of the underlying object
+ // except LaunchParams.
+ assertThat(params.getTask().toString()).isEqualTo(mTask.toString());
+ assertThat(params.getWindowLayout().toString()).isEqualTo(mLayout.toString());
+ assertThat(params.getActivity().getActivityRecord()).isSameInstanceAs(mActvity);
+ assertThat(params.getSource().getActivityRecord()).isSameInstanceAs(mSource);
+ assertThat(params.getCurrentParams().getWindowingMode())
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN);
+ assertThat(params.getOutParams().getWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW);
+ assertThat(params.supportsMultiDisplay()).isEqualTo(mSupportsMultiDisplay);
+ }
+}
diff --git a/builtInServices/tests/src/com/android/server/wm/LaunchParamsWrapperTest.java b/builtInServices/tests/src/com/android/server/wm/LaunchParamsWrapperTest.java
new file mode 100644
index 0000000..3a19199
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/LaunchParamsWrapperTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+/**
+ * This class contains unit tests for the {@link LaunchParamsWrapper}.
+ */
+public final class LaunchParamsWrapperTest {
+ @Test
+ public void create_returnsLaunchParamsWrapper() {
+ LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
+ LaunchParamsWrapper wrapper = LaunchParamsWrapper.create(params);
+ assertThat(wrapper).isNotNull();
+ }
+
+ @Test
+ public void create_returnsNull() {
+ LaunchParamsWrapper wrapper = LaunchParamsWrapper.create(null);
+ assertThat(wrapper).isNull();
+ }
+} \ No newline at end of file
diff --git a/builtInServices/tests/src/com/android/server/wm/RequestWrapperTest.java b/builtInServices/tests/src/com/android/server/wm/RequestWrapperTest.java
new file mode 100644
index 0000000..7e81390
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/RequestWrapperTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+/**
+ * This class contains unit tests for the {@link RequestWrapper}.
+ */
+public final class RequestWrapperTest {
+ @Test
+ public void create_returnsActivityOptionWrapper() {
+ ActivityStarter.Request request = new ActivityStarter.Request();
+ RequestWrapper wrapper = RequestWrapper.create(request);
+ assertThat(wrapper).isNotNull();
+ assertThat(wrapper.getRequest()).isSameInstanceAs(request);
+ assertThat(wrapper.toString()).isEqualTo(request.toString());
+ }
+
+ @Test
+ public void create_returnsNull() {
+ RequestWrapper wrapper = RequestWrapper.create(null);
+ assertThat(wrapper).isNull();
+ }
+}
diff --git a/builtInServices/tests/src/com/android/server/wm/TaskDisplayAreaWrapperTest.java b/builtInServices/tests/src/com/android/server/wm/TaskDisplayAreaWrapperTest.java
new file mode 100644
index 0000000..53c100f
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/TaskDisplayAreaWrapperTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * This class contains unit tests for the {@link TaskDisplayAreaWrapper}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class TaskDisplayAreaWrapperTest {
+ @Mock
+ private TaskDisplayArea mTaskDisplayArea;
+
+ @Test
+ public void create_returnsActivityOptionWrapper() {
+ TaskDisplayAreaWrapper wrapper = TaskDisplayAreaWrapper.create(mTaskDisplayArea);
+ assertThat(wrapper).isNotNull();
+ assertThat(wrapper.getTaskDisplayArea()).isSameInstanceAs(mTaskDisplayArea);
+ assertThat(wrapper.toString()).isEqualTo(mTaskDisplayArea.toString());
+ }
+
+ @Test
+ public void create_returnsNull() {
+ TaskDisplayAreaWrapper wrapper = TaskDisplayAreaWrapper.create(null);
+ assertThat(wrapper).isNull();
+ }
+}
diff --git a/builtInServices/tests/src/com/android/server/wm/TaskWrapperTest.java b/builtInServices/tests/src/com/android/server/wm/TaskWrapperTest.java
new file mode 100644
index 0000000..ce29cd6
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/TaskWrapperTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * This class contains unit tests for the {@link TaskWrapper}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class TaskWrapperTest {
+ @Mock
+ private Task mTask;
+
+ @Test
+ public void create_returnsActivityOptionWrapper() {
+ TaskWrapper wrapper = TaskWrapper.create(mTask);
+ assertThat(wrapper).isNotNull();
+ assertThat(wrapper.toString()).isEqualTo(mTask.toString());
+ }
+
+ @Test
+ public void create_returnsNull() {
+ TaskWrapper wrapper = TaskWrapper.create(null);
+ assertThat(wrapper).isNull();
+ }
+}
diff --git a/builtInServices/tests/src/com/android/server/wm/WindowLayoutWrapperTest.java b/builtInServices/tests/src/com/android/server/wm/WindowLayoutWrapperTest.java
new file mode 100644
index 0000000..0d04d69
--- /dev/null
+++ b/builtInServices/tests/src/com/android/server/wm/WindowLayoutWrapperTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.ActivityInfo;
+import android.view.Gravity;
+
+import org.junit.Test;
+
+/**
+ * This class contains unit tests for the {@link WindowLayoutWrapper}.
+ */
+public final class WindowLayoutWrapperTest {
+ @Test
+ public void create_returnsActivityOptionWrapper() {
+ ActivityInfo.WindowLayout layout = new ActivityInfo.WindowLayout(
+ /* width= */ 1280, /* widthFraction= */ 0.5f,
+ /* height= */ 800, /* heightFraction= */ 1.0f,
+ /* gravity= */ Gravity.CENTER, /* minWidth= */ 400, /* minHeight= */ 300);
+ WindowLayoutWrapper wrapper = WindowLayoutWrapper.create(layout);
+ assertThat(wrapper).isNotNull();
+ assertThat(wrapper.toString()).isEqualTo(layout.toString());
+ }
+
+ @Test
+ public void create_returnsNull() {
+ WindowLayoutWrapper wrapper = WindowLayoutWrapper.create(null);
+ assertThat(wrapper).isNull();
+ }
+}
diff --git a/src/com/android/server/wm/CarLaunchParamsModifier.java b/src/com/android/server/wm/CarLaunchParamsModifier.java
deleted file mode 100644
index 0997ff2..0000000
--- a/src/com/android/server/wm/CarLaunchParamsModifier.java
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.ActivityStarter.Request;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.hardware.display.DisplayManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.util.SparseIntArray;
-import android.view.Display;
-import android.window.WindowContainerToken;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Class to control the assignment of a display for Car while launching a Activity.
- *
- * <p>This one controls which displays users are allowed to launch.
- * The policy should be passed from car service through
- * {@link com.android.internal.car.ICarServiceHelper} binder interfaces. If no policy is set,
- * this module will not change anything for launch process.</p>
- *
- * <p> The policy can only affect which display passenger users can use. Current user, assumed
- * to be a driver user, is allowed to launch any display always.</p>
- */
-public final class CarLaunchParamsModifier implements LaunchParamsController.LaunchParamsModifier {
-
- private static final String TAG = "CAR.LAUNCH";
- private static final boolean DBG = false;
-
- private final Context mContext;
-
- private DisplayManager mDisplayManager; // set only from init()
- private ActivityTaskManagerService mAtm; // set only from init()
-
- private final Object mLock = new Object();
-
- // Always start with USER_SYSTEM as the timing of handleCurrentUserSwitching(USER_SYSTEM) is not
- // guaranteed to be earler than 1st Activity launch.
- @GuardedBy("mLock")
- private int mCurrentDriverUser = UserHandle.USER_SYSTEM;
-
- // TODO: Switch from tracking displays to tracking display areas instead
- /**
- * This one is for holding all passenger (=profile user) displays which are mostly static unless
- * displays are added / removed. Note that {@link #mDisplayToProfileUserMapping} can be empty
- * while user is assigned and that cannot always tell if specific display is for driver or not.
- */
- @GuardedBy("mLock")
- private final ArrayList<Integer> mPassengerDisplays = new ArrayList<>();
-
- /** key: display id, value: profile user id */
- @GuardedBy("mLock")
- private final SparseIntArray mDisplayToProfileUserMapping = new SparseIntArray();
-
- /** key: profile user id, value: display id */
- @GuardedBy("mLock")
- private final SparseIntArray mDefaultDisplayForProfileUser = new SparseIntArray();
-
- @GuardedBy("mLock")
- private boolean mIsSourcePreferred;
-
- @GuardedBy("mLock")
- private List<ComponentName> mSourcePreferredComponents;
-
-
- @VisibleForTesting
- final DisplayManager.DisplayListener mDisplayListener =
- new DisplayManager.DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- // ignore. car service should update whiltelist.
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- synchronized (mLock) {
- mPassengerDisplays.remove(Integer.valueOf(displayId));
- updateProfileUserConfigForDisplayRemovalLocked(displayId);
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- // ignore
- }
- };
-
- private void updateProfileUserConfigForDisplayRemovalLocked(int displayId) {
- mDisplayToProfileUserMapping.delete(displayId);
- int i = mDefaultDisplayForProfileUser.indexOfValue(displayId);
- if (i >= 0) {
- mDefaultDisplayForProfileUser.removeAt(i);
- }
- }
-
- /** Constructor. Can be constructed any time. */
- public CarLaunchParamsModifier(Context context) {
- // This can be very early stage. So postpone interaction with other system until init.
- mContext = context;
- }
-
- /**
- * Initializes all internal stuffs. This should be called only after ATMS, DisplayManagerService
- * are ready.
- */
- public void init() {
- mAtm = (ActivityTaskManagerService) ActivityTaskManager.getService();
- LaunchParamsController controller = mAtm.mTaskSupervisor.getLaunchParamsController();
- controller.registerModifier(this);
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
- mDisplayManager.registerDisplayListener(mDisplayListener,
- new Handler(Looper.getMainLooper()));
- }
-
- /**
- * Sets sourcePreferred configuration. When sourcePreferred is enabled and there is no pre-
- * assigned display for the Activity, CarLauncherParamsModifier will launch the Activity in
- * the display of the source. When sourcePreferredComponents isn't null the sourcePreferred
- * is applied for the sourcePreferredComponents only.
- *
- * @param enableSourcePreferred whether to enable sourcePreferred mode
- * @param sourcePreferredComponents null for all components, or the list of components to apply
- */
- public void setSourcePreferredComponents(boolean enableSourcePreferred,
- @Nullable List<ComponentName> sourcePreferredComponents) {
- synchronized (mLock) {
- mIsSourcePreferred = enableSourcePreferred;
- mSourcePreferredComponents = sourcePreferredComponents;
- if (mSourcePreferredComponents != null) {
- Collections.sort(mSourcePreferredComponents);
- }
- }
- }
-
- /** Notifies user switching. */
- public void handleCurrentUserSwitching(int newUserId) {
- synchronized (mLock) {
- mCurrentDriverUser = newUserId;
- mDefaultDisplayForProfileUser.clear();
- mDisplayToProfileUserMapping.clear();
- }
- }
-
- private void removeUserFromAllowlistsLocked(int userId) {
- for (int i = mDisplayToProfileUserMapping.size() - 1; i >= 0; i--) {
- if (mDisplayToProfileUserMapping.valueAt(i) == userId) {
- mDisplayToProfileUserMapping.removeAt(i);
- }
- }
- mDefaultDisplayForProfileUser.delete(userId);
- }
-
- /** Notifies user stopped. */
- public void handleUserStopped(int stoppedUser) {
- // Note that the current user is never stopped. It always takes switching into
- // non-current user before stopping the user.
- synchronized (mLock) {
- removeUserFromAllowlistsLocked(stoppedUser);
- }
- }
-
- /**
- * Sets display allowlist for the userId. For passenger user, activity will be always launched
- * to a display in the allowlist. If requested display is not in the allowlist, the 1st display
- * in the allowlist will be selected as target display.
- *
- * <p>The allowlist is kept only for profile user. Assigning the current user unassigns users
- * for the given displays.
- */
- public void setDisplayAllowListForUser(int userId, int[] displayIds) {
- if (DBG) {
- Slog.d(TAG, "setDisplayAllowlistForUser userId:" + userId
- + " displays:" + displayIds);
- }
- synchronized (mLock) {
- for (int displayId : displayIds) {
- if (!mPassengerDisplays.contains(displayId)) {
- Slog.w(TAG, "setDisplayAllowlistForUser called with display:" + displayId
- + " not in passenger display list:" + mPassengerDisplays);
- continue;
- }
- if (userId == mCurrentDriverUser) {
- mDisplayToProfileUserMapping.delete(displayId);
- } else {
- mDisplayToProfileUserMapping.put(displayId, userId);
- }
- // now the display cannot be a default display for other user
- int i = mDefaultDisplayForProfileUser.indexOfValue(displayId);
- if (i >= 0) {
- mDefaultDisplayForProfileUser.removeAt(i);
- }
- }
- if (displayIds.length > 0) {
- mDefaultDisplayForProfileUser.put(userId, displayIds[0]);
- } else {
- removeUserFromAllowlistsLocked(userId);
- }
- }
- }
-
- /**
- * Sets displays assigned to passenger. All other displays will be treated as assigned to
- * driver.
- *
- * <p>The 1st display in the array will be considered as a default display to assign
- * for any non-driver user if there is no display assigned for the user. </p>
- */
- public void setPassengerDisplays(int[] displayIdsForPassenger) {
- if (DBG) {
- Slog.d(TAG, "setPassengerDisplays displays:" + displayIdsForPassenger);
- }
- synchronized (mLock) {
- for (int id : displayIdsForPassenger) {
- mPassengerDisplays.remove(Integer.valueOf(id));
- }
- // handle removed displays
- for (int i = 0; i < mPassengerDisplays.size(); i++) {
- int displayId = mPassengerDisplays.get(i);
- updateProfileUserConfigForDisplayRemovalLocked(displayId);
- }
- mPassengerDisplays.clear();
- mPassengerDisplays.ensureCapacity(displayIdsForPassenger.length);
- for (int id : displayIdsForPassenger) {
- mPassengerDisplays.add(id);
- }
- }
- }
-
- /**
- * Decides display to assign while an Activity is launched.
- *
- * <p>For current user (=driver), launching to any display is allowed as long as system
- * allows it.</p>
- *
- * <p>For private display, do not change anything as private display has its own logic.</p>
- *
- * <p>For passenger displays, only run in allowed displays. If requested display is not
- * allowed, change to the 1st allowed display.</p>
- */
- @Override
- @Result
- public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
- @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
- ActivityOptions options, @Nullable Request request, int phase,
- LaunchParamsController.LaunchParams currentParams,
- LaunchParamsController.LaunchParams outParams) {
- int userId;
- if (task != null) {
- userId = task.mUserId;
- } else if (activity != null) {
- userId = activity.mUserId;
- } else {
- Slog.w(TAG, "onCalculate, cannot decide user");
- return RESULT_SKIP;
- }
- // DisplayArea where user wants to launch the Activity.
- TaskDisplayArea originalDisplayArea = currentParams.mPreferredTaskDisplayArea;
- // DisplayArea where CarLaunchParamsModifier targets to launch the Activity.
- TaskDisplayArea targetDisplayArea = null;
- if (DBG) {
- Slog.d(TAG, "onCalculate, userId:" + userId
- + " original displayArea:" + originalDisplayArea
- + " ActivityOptions:" + options);
- }
- // If originalDisplayArea is set, respect that before ActivityOptions check.
- if (originalDisplayArea == null) {
- if (options != null) {
- WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
- if (daToken != null) {
- originalDisplayArea = (TaskDisplayArea) WindowContainer.fromBinder(
- daToken.asBinder());
- } else {
- int originalDisplayId = options.getLaunchDisplayId();
- if (originalDisplayId != Display.INVALID_DISPLAY) {
- originalDisplayArea = getDefaultTaskDisplayAreaOnDisplay(originalDisplayId);
- }
- }
- }
- }
- decision:
- synchronized (mLock) {
- if (originalDisplayArea == null // No specified DisplayArea to launch the Activity
- && mIsSourcePreferred && source != null
- && (mSourcePreferredComponents == null || Collections.binarySearch(
- mSourcePreferredComponents, activity.info.getComponentName()) >= 0)) {
- targetDisplayArea = source.noDisplay ? source.mHandoverTaskDisplayArea
- : source.getDisplayArea();
- } else if (originalDisplayArea == null
- && task == null // launching as a new task
- && source != null && !source.getDisplayContent().isTrusted()
- && ((activity.info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0)) {
- if (DBG) {
- Slog.d(TAG, "Disallow launch on virtual display for not-embedded activity.");
- }
- targetDisplayArea = getDefaultTaskDisplayAreaOnDisplay(Display.DEFAULT_DISPLAY);
- }
- if (userId == mCurrentDriverUser) {
- // Respect the existing DisplayArea.
- break decision;
- }
- if (userId == UserHandle.USER_SYSTEM) {
- // This will be only allowed if it has FLAG_SHOW_FOR_ALL_USERS.
- // The flag is not immediately accessible here so skip the check.
- // But other WM policy will enforce it.
- break decision;
- }
- // Now user is a passenger.
- if (mPassengerDisplays.isEmpty()) {
- // No displays for passengers. This could be old user and do not do anything.
- break decision;
- }
- if (targetDisplayArea == null) {
- if (originalDisplayArea != null) {
- targetDisplayArea = originalDisplayArea;
- } else {
- targetDisplayArea = getDefaultTaskDisplayAreaOnDisplay(Display.DEFAULT_DISPLAY);
- }
- }
- Display display = targetDisplayArea.mDisplayContent.getDisplay();
- if ((display.getFlags() & Display.FLAG_PRIVATE) != 0) {
- // private display should follow its own restriction rule.
- break decision;
- }
- if (display.getType() == Display.TYPE_VIRTUAL) {
- // TODO(b/132903422) : We need to update this after the bug is resolved.
- // For now, don't change anything.
- break decision;
- }
- int userForDisplay = mDisplayToProfileUserMapping.get(display.getDisplayId(),
- UserHandle.USER_NULL);
- if (userForDisplay == userId) {
- break decision;
- }
- targetDisplayArea = getAlternativeDisplayAreaForPassengerLocked(
- userId, activity, request);
- }
- if (targetDisplayArea != null && originalDisplayArea != targetDisplayArea) {
- Slog.i(TAG, "Changed launching display, user:" + userId
- + " requested display area:" + originalDisplayArea
- + " target display area:" + targetDisplayArea);
- outParams.mPreferredTaskDisplayArea = targetDisplayArea;
- return RESULT_DONE;
- } else {
- return RESULT_SKIP;
- }
- }
-
- @Nullable
- private TaskDisplayArea getAlternativeDisplayAreaForPassengerLocked(int userId,
- @NonNull ActivityRecord activityRecord, @Nullable Request request) {
- TaskDisplayArea sourceDisplayArea = sourceDisplayArea(userId, activityRecord, request);
-
- return sourceDisplayArea != null ? sourceDisplayArea : fallbackDisplayArea(userId);
- }
-
- @VisibleForTesting
- @Nullable
- TaskDisplayArea getDefaultTaskDisplayAreaOnDisplay(int displayId) {
- DisplayContent dc = mAtm.mRootWindowContainer.getDisplayContentOrCreate(displayId);
- if (dc == null) {
- return null;
- }
- return dc.getDefaultTaskDisplayArea();
- }
-
- /**
- * Calculates the {@link TaskDisplayArea} for the source of the request. The source is
- * calculated implicitly from the request or the activity record.
- *
- * @param userId ID of the current active user
- * @param activityRecord {@link ActivityRecord} that is to be shown
- * @param request {@link Request} data for showing the {@link ActivityRecord}
- * @return {@link TaskDisplayArea} First non {@code null} candidate display area that is allowed
- * for the user. It is allowed if the display has been added to the profile mapping.
- */
- @Nullable
- private TaskDisplayArea sourceDisplayArea(int userId, @NonNull ActivityRecord activityRecord,
- @Nullable Request request) {
- List<WindowProcessController> candidateControllers = candidateControllers(activityRecord,
- request);
-
- for (int i = 0; i < candidateControllers.size(); i++) {
- WindowProcessController controller = candidateControllers.get(i);
- TaskDisplayArea candidate = controller.getTopActivityDisplayArea();
- int displayId = candidate != null ? candidate.getDisplayId() : Display.INVALID_DISPLAY;
- int userForDisplay = mDisplayToProfileUserMapping.get(displayId, UserHandle.USER_NULL);
- if (userForDisplay == userId) {
- return candidate;
- }
- }
- return null;
- }
-
- /**
- * Calculates a list of {@link WindowProcessController} that can calculate the
- * {@link TaskDisplayArea} to house the {@link ActivityRecord}. Controllers are calculated since
- * calculating the display can be expensive. The list is ordered in the
- * following way
- * <ol>
- * <li>Controller for the activity record from the process name and app uid</li>
- * <li>Controller for the activity that is launching the given record</li>
- * <li>Controller for the actual process that is launching the record</li>
- * </ol>
- *
- * @param activityRecord {@link ActivityRecord} that is to be shown
- * @param request {@link Request} data for showing the {@link ActivityRecord}
- * @return {@link List} of {@link WindowProcessController} ordered by preference to be shown
- */
- private List<WindowProcessController> candidateControllers(
- @NonNull ActivityRecord activityRecord, @Nullable Request request) {
- WindowProcessController firstController = mAtm.getProcessController(
- activityRecord.getProcessName(), activityRecord.getUid());
-
- WindowProcessController secondController = mAtm.getProcessController(
- activityRecord.getLaunchedFromPid(), activityRecord.getLaunchedFromUid());
-
- WindowProcessController thirdController = request == null ? null :
- mAtm.getProcessController(request.realCallingPid, request.realCallingUid);
-
- List<WindowProcessController> candidates = new ArrayList<>(3);
-
- if (firstController != null) {
- candidates.add(firstController);
- }
- if (secondController != null) {
- candidates.add(secondController);
- }
- if (thirdController != null) {
- candidates.add(thirdController);
- }
-
- return candidates;
- }
-
- /**
- * Return a {@link TaskDisplayArea} that can be used if a source display area is not found.
- * First check the default display for the user. If it is absent select the first passenger
- * display if present. If both are absent return {@code null}
- *
- * @param userId ID of the active user
- * @return {@link TaskDisplayArea} that is recommended when a display area is not specified
- */
- @Nullable
- private TaskDisplayArea fallbackDisplayArea(int userId) {
- int displayIdForUserProfile = mDefaultDisplayForProfileUser.get(userId,
- Display.INVALID_DISPLAY);
- if (displayIdForUserProfile != Display.INVALID_DISPLAY) {
- int displayId = mDefaultDisplayForProfileUser.get(userId);
- return getDefaultTaskDisplayAreaOnDisplay(displayId);
- }
-
- if (!mPassengerDisplays.isEmpty()) {
- int displayId = mPassengerDisplays.get(0);
- return getDefaultTaskDisplayAreaOnDisplay(displayId);
- }
-
- return null;
- }
-
-}
diff --git a/src/jni/com_android_internal_car_CarServiceHelperService.cpp b/src/jni/com_android_internal_car_CarServiceHelperService.cpp
deleted file mode 100644
index 3942b77..0000000
--- a/src/jni/com_android_internal_car_CarServiceHelperService.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "CarServiceHelperService-JNI"
-
-//#define LOG_NDEBUG 0
-
-#include "jni.h"
-
-#include <nativehelper/JNIHelp.h>
-#include <suspend/autosuspend.h>
-#include <utils/Log.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static jint nativeForceSuspend(JNIEnv* /* env */, jclass /* clazz */, jint timeoutMs) {
- jint ret = autosuspend_force_suspend(timeoutMs);
- ALOGD("nativeForceSuspend returned %d", ret);
- return ret;
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gCarServiceHelperServiceMethods[] = {
- /* name, signature, funcPtr */
- { "nativeForceSuspend", "(I)I",
- (void*) nativeForceSuspend },
-};
-
-int register_android_internal_car_CarServiceHelperService(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "com/android/internal/car/CarServiceHelperService",
- gCarServiceHelperServiceMethods, NELEM(gCarServiceHelperServiceMethods));
- (void) res; // Faked use when LOG_NDEBUG.
- LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- return 0;
-}
-
-} /* namespace android */
-
-
-using namespace android;
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
-{
- JNIEnv* env = NULL;
- jint result = -1;
-
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGE("GetEnv failed!");
- return result;
- }
- ALOG_ASSERT(env, "Could not retrieve the env!");
-
- register_android_internal_car_CarServiceHelperService(env);
- return JNI_VERSION_1_4;
-}
diff --git a/updatableServices/Android.bp b/updatableServices/Android.bp
new file mode 100644
index 0000000..2adcba7
--- /dev/null
+++ b/updatableServices/Android.bp
@@ -0,0 +1,31 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "car-frameworks-service-module",
+ installable: true,
+ libs: [
+ "android.car",
+ "android.car.builtin",
+ "car-frameworks-service",
+ "framework-annotations-lib",
+ "modules-utils-preconditions",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ sdk_version: "module_current",
+ min_sdk_version: "31",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.car.framework"
+ ],
+ product_variables: {
+ pdk: {
+ enabled: false,
+ },
+ },
+}
+
diff --git a/updatableServices/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImpl.java b/updatableServices/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImpl.java
new file mode 100644
index 0000000..0b3b094
--- /dev/null
+++ b/updatableServices/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImpl.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.car.updatable;
+
+import static com.android.car.internal.SystemConstants.ICAR_SYSTEM_SERVER_CLIENT;
+import static com.android.car.internal.common.CommonConstants.CAR_SERVICE_INTERFACE;
+
+import android.annotation.Nullable;
+import android.car.ICar;
+import android.car.ICarResultReceiver;
+import android.car.builtin.os.UserManagerHelper;
+import android.car.builtin.util.EventLogHelper;
+import android.car.builtin.util.Slogf;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+
+import com.android.car.internal.ICarServiceHelper;
+import com.android.car.internal.ICarSystemServerClient;
+import com.android.car.internal.util.IndentingPrintWriter;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.car.CarServiceHelperInterface;
+import com.android.internal.car.CarServiceHelperServiceUpdatable;
+import java.io.File;
+import com.android.server.wm.CarLaunchParamsModifierInterface;
+import com.android.server.wm.CarLaunchParamsModifierUpdatable;
+import com.android.server.wm.CarLaunchParamsModifierUpdatableImpl;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+
+/**
+ * Implementation of the abstract class CarServiceHelperUpdatable
+ */
+public final class CarServiceHelperServiceUpdatableImpl
+ implements CarServiceHelperServiceUpdatable, Executor {
+
+ @VisibleForTesting
+ static final String TAG = "CarServiceHelper";
+
+ private static final boolean DBG = false;
+
+ private static final String PROP_RESTART_RUNTIME = "ro.car.recovery.restart_runtime.enabled";
+
+ private static final long CAR_SERVICE_BINDER_CALL_TIMEOUT_MS = 15_000;
+
+ private final Runnable mCallbackForCarServiceUnresponsiveness;
+
+ // exit code for
+ private static final int STATUS_CODE_To_EXIT = 10;
+
+ private static final String CAR_SERVICE_PACKAGE = "com.android.car";
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private ICar mCarServiceBinder;
+
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread = new HandlerThread(
+ CarServiceHelperServiceUpdatableImpl.class.getSimpleName());
+
+ private final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();
+
+ private final CarServiceConnectedCallback mCarServiceConnectedCallback =
+ new CarServiceConnectedCallback();
+
+ private final CarServiceProxy mCarServiceProxy;
+
+ private final CarServiceHelperInterface mCarServiceHelperInterface;
+
+ private final CarLaunchParamsModifierUpdatableImpl mCarLaunchParamsModifierUpdatable;
+
+ public CarServiceHelperServiceUpdatableImpl(Context context,
+ CarServiceHelperInterface carServiceHelperInterface,
+ CarLaunchParamsModifierInterface carLaunchParamsModifierInterface) {
+ this(context, carServiceHelperInterface, carLaunchParamsModifierInterface,
+ /* carServiceProxy= */ null);
+ }
+
+ @VisibleForTesting
+ CarServiceHelperServiceUpdatableImpl(Context context,
+ CarServiceHelperInterface carServiceHelperInterface,
+ CarLaunchParamsModifierInterface carLaunchParamsModifierInterface,
+ @Nullable CarServiceProxy carServiceProxy) {
+ mContext = context;
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mCarServiceHelperInterface = carServiceHelperInterface;
+ mCarLaunchParamsModifierUpdatable = new CarLaunchParamsModifierUpdatableImpl(
+ carLaunchParamsModifierInterface);
+ // carServiceProxy is Nullable because it is not possible to construct carServiceProxy with
+ // "this" object in the previous constructor as CarServiceHelperServiceUpdatableImpl has
+ // not been fully constructed.
+ mCarServiceProxy = carServiceProxy == null ? new CarServiceProxy(this) : carServiceProxy;
+ mCallbackForCarServiceUnresponsiveness = () -> handleCarServiceUnresponsive();
+ }
+
+ private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ if (DBG) Slogf.d(TAG, "onServiceConnected: %s", iBinder);
+ handleCarServiceConnection(iBinder);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ handleCarServiceCrash();
+ }
+ };
+
+ @Override
+ public void onStart() {
+ Intent intent = new Intent(CAR_SERVICE_INTERFACE).setPackage(CAR_SERVICE_PACKAGE);
+ Context userContext = mContext.createContextAsUser(UserHandle.SYSTEM, /* flags= */ 0);
+ if (!userContext.bindService(intent, Context.BIND_AUTO_CREATE, this,
+ mCarServiceConnection)) {
+ Slogf.wtf(TAG, "cannot start car service");
+ }
+ }
+
+ @Override // From Executor
+ public void execute(Runnable command) {
+ mHandler.post(command);
+ }
+
+ @Override
+ public void onUserRemoved(UserHandle user) {
+ mCarServiceProxy.onUserRemoved(user);
+ }
+
+ @Override
+ public void onFactoryReset(BiConsumer<Integer, Bundle> callback) {
+ ICarResultReceiver resultReceiver = new ICarResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ callback.accept(resultCode, resultData);
+
+ }
+ };
+ mCarServiceProxy.onFactoryReset(resultReceiver);
+ }
+
+ @Override
+ public void initBootUser() {
+ mCarServiceProxy.initBootUser();
+ }
+
+ @Override
+ public CarLaunchParamsModifierUpdatable getCarLaunchParamsModifierUpdatable() {
+ return mCarLaunchParamsModifierUpdatable;
+ }
+
+ @VisibleForTesting
+ void handleCarServiceConnection(IBinder iBinder) {
+ synchronized (mLock) {
+ if (mCarServiceBinder == ICar.Stub.asInterface(iBinder)) {
+ return; // already connected.
+ }
+ if (DBG) {
+ Slogf.d(TAG, "car service binder changed, was %s new: %s", mCarServiceBinder,
+ iBinder);
+ }
+ mCarServiceBinder = ICar.Stub.asInterface(iBinder);
+ Slogf.i(TAG, "**CarService connected**");
+ }
+
+ EventLogHelper.writeCarHelperServiceConnected();
+
+ // Post mCallbackForCarServiceUnresponsiveness before setting system server connection
+ // because CarService may respond before the sendSetSystemServerConnectionsCall call
+ // returns and try to remove mCallbackForCarServiceUnresponsiveness from the handler.
+ // Thus, posting this callback after setting system server connection may result in a race
+ // condition where the callback is never removed from the handler.
+ mHandler.removeCallbacks(mCallbackForCarServiceUnresponsiveness);
+ mHandler.postDelayed(mCallbackForCarServiceUnresponsiveness,
+ CAR_SERVICE_BINDER_CALL_TIMEOUT_MS);
+
+ sendSetSystemServerConnectionsCall();
+ }
+
+ @VisibleForTesting
+ void handleCarServiceCrash() {
+ // Recovery behavior. Kill the system server and reset
+ // everything if enabled by the property.
+ boolean restartOnServiceCrash = SystemProperties.getBoolean(PROP_RESTART_RUNTIME, false);
+ mHandler.removeCallbacks(mCallbackForCarServiceUnresponsiveness);
+
+ mCarServiceHelperInterface.dumpServiceStacks();
+ if (restartOnServiceCrash) {
+ Slogf.w(TAG, "*** CARHELPER KILLING SYSTEM PROCESS: CarService crash");
+ Slogf.w(TAG, "*** GOODBYE!");
+ Process.killProcess(Process.myPid());
+ System.exit(STATUS_CODE_To_EXIT);
+ } else {
+ Slogf.w(TAG, "*** CARHELPER ignoring: CarService crash");
+ }
+ }
+
+ private void sendSetSystemServerConnectionsCall() {
+ ICar binder;
+ synchronized (mLock) {
+ binder = mCarServiceBinder;
+ }
+ try {
+ binder.setSystemServerConnections(mHelper, mCarServiceConnectedCallback);
+ } catch (RemoteException e) {
+ Slogf.w(TAG, e, "RemoteException from car service");
+ handleCarServiceCrash();
+ } catch (RuntimeException e) {
+ Slogf.wtf(TAG, e, "Exception calling setSystemServerConnections");
+ throw e;
+ }
+ }
+
+ private void handleCarServiceUnresponsive() {
+ // This should not happen. Calling this method means ICarSystemServerClient binder is not
+ // returned after service connection. and CarService has not connected in the given time.
+ Slogf.w(TAG, "*** CARHELPER KILLING SYSTEM PROCESS: CarService unresponsive.");
+ Slogf.w(TAG, "*** GOODBYE!");
+ Process.killProcess(Process.myPid());
+ System.exit(STATUS_CODE_To_EXIT);
+ }
+
+ @Override
+ public void sendUserLifecycleEvent(int eventType, UserHandle userFrom, UserHandle userTo) {
+ mCarServiceProxy.sendUserLifecycleEvent(eventType,
+ userFrom == null ? UserManagerHelper.USER_NULL : userFrom.getIdentifier(),
+ userTo.getIdentifier());
+ }
+
+ @Override
+ public void dump(PrintWriter writer, String[] args) {
+ if (args != null && args.length > 0 && "--user-metrics-only".equals(args[0])) {
+ mCarServiceProxy.dumpUserMetrics(new IndentingPrintWriter(writer));
+ return;
+ }
+
+ if (args != null && args.length > 0 && "--dump-service-stacks".equals(args[0])) {
+ File file = mCarServiceHelperInterface.dumpServiceStacks();
+ if (file != null) {
+ writer.printf("dumpServiceStacks ANR file path=%s\n", file.getAbsolutePath());
+ } else {
+ writer.printf("dumpServiceStacks no ANR file.\n");
+ }
+ return;
+ }
+
+ mCarServiceProxy.dump(new IndentingPrintWriter(writer));
+ }
+
+ private final class ICarServiceHelperImpl extends ICarServiceHelper.Stub {
+
+ @Override
+ public void setDisplayAllowlistForUser(int userId, int[] displayIds) {
+ mCarLaunchParamsModifierUpdatable.setDisplayAllowListForUser(userId, displayIds);
+ }
+
+ @Override
+ public void setPassengerDisplays(int[] displayIdsForPassenger) {
+ mCarLaunchParamsModifierUpdatable.setPassengerDisplays(displayIdsForPassenger);
+ }
+
+ @Override
+ public void setSourcePreferredComponents(boolean enableSourcePreferred,
+ @Nullable List<ComponentName> sourcePreferredComponents) {
+ mCarLaunchParamsModifierUpdatable.setSourcePreferredComponents(
+ enableSourcePreferred, sourcePreferredComponents);
+ }
+
+ @Override
+ public int setPersistentActivity(ComponentName activity, int displayId, int featureId) {
+ return mCarLaunchParamsModifierUpdatable.setPersistentActivity(
+ activity, displayId, featureId);
+ }
+
+ @Override
+ public void setSafetyMode(boolean safe) {
+ mCarServiceHelperInterface.setSafetyMode(safe);
+ }
+
+ @Override
+ public UserHandle createUserEvenWhenDisallowed(String name, String userType, int flags) {
+ return mCarServiceHelperInterface.createUserEvenWhenDisallowed(name, userType, flags);
+ }
+
+ @Override
+ public void sendInitialUser(UserHandle user) {
+ mCarServiceProxy.saveInitialUser(user);
+ }
+ }
+
+ private final class CarServiceConnectedCallback extends ICarResultReceiver.Stub {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ mHandler.removeCallbacks(mCallbackForCarServiceUnresponsiveness);
+
+ IBinder binder;
+ if (resultData == null
+ || (binder = resultData.getBinder(ICAR_SYSTEM_SERVER_CLIENT)) == null) {
+ Slogf.wtf(TAG, "setSystemServerConnections return NULL data or Binder.");
+ handleCarServiceUnresponsive();
+ return;
+ }
+
+ ICarSystemServerClient carService = ICarSystemServerClient.Stub.asInterface(binder);
+ mCarServiceProxy.handleCarServiceConnection(carService);
+ }
+ }
+}
diff --git a/src/com/android/internal/car/CarServiceProxy.java b/updatableServices/src/com/android/internal/car/updatable/CarServiceProxy.java
index 42c344b..0824755 100644
--- a/src/com/android/internal/car/CarServiceProxy.java
+++ b/updatableServices/src/com/android/internal/car/updatable/CarServiceProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.car;
+package com.android.internal.car.updatable;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STARTING;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
@@ -27,23 +27,22 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.content.pm.UserInfo;
+import android.car.ICarResultReceiver;
+import android.car.builtin.os.UserManagerHelper;
+import android.car.builtin.util.Slogf;
+import android.car.builtin.util.TimingsTraceLog;
import android.os.RemoteException;
-import android.os.Trace;
import android.os.UserHandle;
-import android.util.DebugUtils;
-import android.util.IndentingPrintWriter;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.car.internal.ICarSystemServerClient;
import com.android.car.internal.common.CommonConstants.UserLifecycleEventType;
+import com.android.car.internal.util.DebugUtils;
+import com.android.car.internal.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.IResultReceiver;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.server.SystemService.TargetUser;
-import com.android.server.utils.TimingsTraceAndSlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -89,10 +88,13 @@ final class CarServiceProxy {
@Retention(RetentionPolicy.SOURCE)
public @interface PendingOperationId{}
+ @VisibleForTesting
+ static final String TAG = CarServiceProxy.class.getSimpleName();
+
+ private static final long TRACE_TAG_SYSTEM_SERVER = 1L << 19;
private static final boolean DBG = false;
- private static final String TAG = CarServiceProxy.class.getSimpleName();
- private static final long LIFECYCLE_TIMESTAMP_IGNORE = 0;
+ private static final int USER_SYSTEM = UserHandle.SYSTEM.getIdentifier();
private final Object mLock = new Object();
@@ -100,10 +102,10 @@ final class CarServiceProxy {
private boolean mCarServiceCrashed;
@UserIdInt
@GuardedBy("mLock")
- private int mLastSwitchedUser = UserHandle.USER_NULL;
+ private int mLastSwitchedUser = UserManagerHelper.USER_NULL;
@UserIdInt
@GuardedBy("mLock")
- private int mPreviousUserOfLastSwitchedUser = UserHandle.USER_NULL;
+ private int mPreviousUserOfLastSwitchedUser = UserManagerHelper.USER_NULL;
// Key: user id, value: life-cycle
@GuardedBy("mLock")
private final SparseIntArray mLastUserLifecycle = new SparseIntArray();
@@ -114,19 +116,21 @@ final class CarServiceProxy {
@GuardedBy("mLock")
private ICarSystemServerClient mCarService;
- private final CarServiceHelperService mCarServiceHelperService;
+ private final CarServiceHelperServiceUpdatableImpl mCarServiceHelperServiceUpdatableImpl;
private final UserMetrics mUserMetrics = new UserMetrics();
+ @GuardedBy("mLock")
+ private UserHandle mInitialUser;
- CarServiceProxy(CarServiceHelperService carServiceHelperService) {
- mCarServiceHelperService = carServiceHelperService;
+ CarServiceProxy(CarServiceHelperServiceUpdatableImpl carServiceHelperServiceUpdatableImpl) {
+ mCarServiceHelperServiceUpdatableImpl = carServiceHelperServiceUpdatableImpl;
}
/**
* Handles new CarService Connection.
*/
void handleCarServiceConnection(ICarSystemServerClient carService) {
- Slog.i(TAG, "CarService connected.");
- TimingsTraceAndSlog t = newTimingsTraceAndSlog();
+ Slogf.i(TAG, "CarService connected.");
+ TimingsTraceLog t = newTimingsTraceLog();
t.traceBegin("handleCarServiceConnection");
synchronized (mLock) {
mCarService = carService;
@@ -136,9 +140,37 @@ final class CarServiceProxy {
runQueuedOperationLocked(PO_ON_FACTORY_RESET);
}
sendLifeCycleEvents();
+ sendInitialUser();
t.traceEnd();
}
+ private void sendInitialUser() {
+ UserHandle initialUser;
+ ICarSystemServerClient carService;
+ synchronized (mLock) {
+ initialUser = mInitialUser;
+ carService = mCarService;
+ }
+ if (initialUser != null && carService != null) {
+ try {
+ carService.setInitialUser(initialUser);
+ } catch (RemoteException e) {
+ Slogf.w(TAG, "RemoteException from car service while calling setInitialUser.", e);
+ }
+ } else {
+ Slogf.i(TAG, "Didn't send Initial User, User: %s, CarService: %s", initialUser,
+ carService);
+ }
+ }
+
+ void saveInitialUser(UserHandle user) {
+ synchronized (mLock) {
+ if (user != null || user.getIdentifier() != UserManagerHelper.USER_NULL) {
+ mInitialUser = user;
+ }
+ }
+ }
+
@GuardedBy("mLock")
private void runQueuedOperationLocked(@PendingOperationId int operationId) {
PendingOperation pendingOperation = mPendingOperations.get(operationId);
@@ -147,7 +179,7 @@ final class CarServiceProxy {
return;
}
if (DBG) {
- Slog.d(TAG, "No queued operation of type " + pendingOperationToString(operationId));
+ Slogf.d(TAG, "No queued operation of type " + pendingOperationToString(operationId));
}
}
@@ -161,13 +193,14 @@ final class CarServiceProxy {
}
// Send user0 events first
- int user0Lifecycle = lastUserLifecycle.get(UserHandle.USER_SYSTEM);
- boolean user0IsCurrent = lastSwitchedUser == UserHandle.USER_SYSTEM;
+ int user0Lifecycle = lastUserLifecycle.get(USER_SYSTEM);
+ boolean user0IsCurrent = lastSwitchedUser == USER_SYSTEM;
// If user0Lifecycle is 0, then no life-cycle event received yet.
if (user0Lifecycle != 0) {
- sendAllLifecyleToUser(UserHandle.USER_SYSTEM, user0Lifecycle, user0IsCurrent);
+ sendAllLifecyleToUser(USER_SYSTEM, user0Lifecycle,
+ user0IsCurrent);
}
- lastUserLifecycle.delete(UserHandle.USER_SYSTEM);
+ lastUserLifecycle.delete(USER_SYSTEM);
// Send current user events next
if (!user0IsCurrent) {
@@ -192,28 +225,28 @@ final class CarServiceProxy {
private void sendAllLifecyleToUser(@UserIdInt int userId, int lifecycle,
boolean isCurrentUser) {
if (DBG) {
- Slog.d(TAG, "sendAllLifecyleToUser, user:" + userId + " lifecycle:" + lifecycle);
+ Slogf.d(TAG, "sendAllLifecyleToUser, user:" + userId + " lifecycle:" + lifecycle);
}
if (lifecycle >= USER_LIFECYCLE_EVENT_TYPE_STARTING) {
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_STARTING, UserHandle.USER_NULL,
- userId);
+ sendUserLifecycleEventInternal(USER_LIFECYCLE_EVENT_TYPE_STARTING,
+ UserManagerHelper.USER_NULL, userId);
}
- if (isCurrentUser && userId != UserHandle.USER_SYSTEM) {
+ if (isCurrentUser && userId != USER_SYSTEM) {
synchronized (mLock) {
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
+ sendUserLifecycleEventInternal(USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
mPreviousUserOfLastSwitchedUser, userId);
}
}
if (lifecycle >= USER_LIFECYCLE_EVENT_TYPE_UNLOCKING) {
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING, UserHandle.USER_NULL,
- userId);
+ sendUserLifecycleEventInternal(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING,
+ UserManagerHelper.USER_NULL, userId);
}
if (lifecycle >= USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) {
- sendUserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, UserHandle.USER_NULL,
- userId);
+ sendUserLifecycleEventInternal(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED,
+ UserManagerHelper.USER_NULL, userId);
}
}
@@ -221,7 +254,7 @@ final class CarServiceProxy {
* Initializes boot user.
*/
void initBootUser() {
- if (DBG) Slog.d(TAG, "initBootUser()");
+ if (DBG) Slogf.d(TAG, "initBootUser()");
saveOrRun(PO_INIT_BOOT_USER);
}
@@ -230,8 +263,8 @@ final class CarServiceProxy {
/**
* Callback to indifcate the given user was removed.
*/
- void onUserRemoved(@NonNull UserInfo user) {
- if (DBG) Slog.d(TAG, "onUserRemoved(): " + user.toFullString());
+ void onUserRemoved(@NonNull UserHandle user) {
+ if (DBG) Slogf.d(TAG, "onUserRemoved(): " + user);
saveOrRun(PO_ON_USER_REMOVED, user);
}
@@ -240,8 +273,8 @@ final class CarServiceProxy {
/**
* Callback to ask user to confirm if it's ok to factory reset the device.
*/
- void onFactoryReset(@NonNull IResultReceiver callback) {
- if (DBG) Slog.d(TAG, "onFactoryReset(): " + callback);
+ void onFactoryReset(@NonNull ICarResultReceiver callback) {
+ if (DBG) Slogf.d(TAG, "onFactoryReset(): " + callback);
saveOrRun(PO_ON_FACTORY_RESET, callback);
}
@@ -254,7 +287,7 @@ final class CarServiceProxy {
synchronized (mLock) {
if (mCarService == null) {
if (DBG) {
- Slog.d(TAG, "CarService null. Operation "
+ Slogf.d(TAG, "CarService null. Operation "
+ pendingOperationToString(operationId)
+ (value == null ? "" : "(" + value + ")") + " deferred.");
}
@@ -272,20 +305,22 @@ final class CarServiceProxy {
@GuardedBy("mLock")
private void runLocked(@PendingOperationId int operationId, @Nullable Object value) {
- if (DBG) Slog.d(TAG, "runLocked(): " + pendingOperationToString(operationId) + "/" + value);
+ if (DBG) {
+ Slogf.d(TAG, "runLocked(): " + pendingOperationToString(operationId) + "/" + value);
+ }
try {
if (isServiceCrashedLoggedLocked(operationId)) {
return;
}
sendCarServiceActionLocked(operationId, value);
if (operationId == PO_ON_FACTORY_RESET) {
- if (DBG) Slog.d(TAG, "NOT removing " + pendingOperationToString(operationId));
+ if (DBG) Slogf.d(TAG, "NOT removing " + pendingOperationToString(operationId));
return;
}
- if (DBG) Slog.d(TAG, "removing " + pendingOperationToString(operationId));
+ if (DBG) Slogf.d(TAG, "removing " + pendingOperationToString(operationId));
mPendingOperations.delete(operationId);
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException from car service", e);
+ Slogf.w(TAG, "RemoteException from car service", e);
handleCarServiceCrash();
}
}
@@ -297,22 +332,22 @@ final class CarServiceProxy {
if (pendingOperation == null) {
pendingOperation = new PendingOperation(operationId, value);
- if (DBG) Slog.d(TAG, "Created " + pendingOperation);
+ if (DBG) Slogf.d(TAG, "Created " + pendingOperation);
mPendingOperations.put(operationId, pendingOperation);
return;
}
switch (operationId) {
case PO_ON_USER_REMOVED:
- Preconditions.checkArgument((value instanceof UserInfo),
+ Preconditions.checkArgument((value instanceof UserHandle),
"invalid value passed to ON_USER_REMOVED", value);
if (pendingOperation.value instanceof ArrayList) {
- if (DBG) Slog.d(TAG, "Adding " + value + " to existing " + pendingOperation);
+ if (DBG) Slogf.d(TAG, "Adding " + value + " to existing " + pendingOperation);
((ArrayList) pendingOperation.value).add(value);
- } else if (pendingOperation.value instanceof UserInfo) {
+ } else if (pendingOperation.value instanceof UserHandle) {
ArrayList<Object> list = new ArrayList<>(2);
list.add(pendingOperation.value);
list.add(value);
- if (DBG) Slog.d(TAG, "Converting " + pendingOperation.value + " to " + list);
+ if (DBG) Slogf.d(TAG, "Converting " + pendingOperation.value + " to " + list);
pendingOperation.value = list;
} else {
throw new IllegalStateException("Invalid value for ON_USER_REMOVED: " + value);
@@ -320,12 +355,12 @@ final class CarServiceProxy {
break;
case PO_ON_FACTORY_RESET:
PendingOperation newOperation = new PendingOperation(operationId, value);
- if (DBG) Slog.d(TAG, "Replacing " + pendingOperation + " by " + newOperation);
+ if (DBG) Slogf.d(TAG, "Replacing " + pendingOperation + " by " + newOperation);
mPendingOperations.put(operationId, newOperation);
break;
default:
if (DBG) {
- Slog.d(TAG, "Already saved operation of type "
+ Slogf.d(TAG, "Already saved operation of type "
+ pendingOperationToString(operationId));
}
}
@@ -335,7 +370,7 @@ final class CarServiceProxy {
private void sendCarServiceActionLocked(@PendingOperationId int operationId,
@Nullable Object value) throws RemoteException {
if (DBG) {
- Slog.d(TAG, "sendCarServiceActionLocked: Operation "
+ Slogf.d(TAG, "sendCarServiceActionLocked: Operation "
+ pendingOperationToString(operationId));
}
switch (operationId) {
@@ -345,7 +380,7 @@ final class CarServiceProxy {
case PO_ON_USER_REMOVED:
if (value instanceof ArrayList) {
ArrayList<Object> list = (ArrayList<Object>) value;
- if (DBG) Slog.d(TAG, "Sending " + list.size() + " onUserRemoved() calls");
+ if (DBG) Slogf.d(TAG, "Sending " + list.size() + " onUserRemoved() calls");
for (Object user: list) {
onUserRemovedLocked(user);
}
@@ -354,58 +389,56 @@ final class CarServiceProxy {
}
break;
case PO_ON_FACTORY_RESET:
- mCarService.onFactoryReset((IResultReceiver) value);
+ mCarService.onFactoryReset((ICarResultReceiver) value);
break;
default:
- Slog.wtf(TAG, "Invalid Operation. OperationId -" + operationId);
+ Slogf.wtf(TAG, "Invalid Operation. OperationId -" + operationId);
}
}
@GuardedBy("mLock")
private void onUserRemovedLocked(@NonNull Object value) throws RemoteException {
- Preconditions.checkArgument((value instanceof UserInfo),
+ Preconditions.checkArgument((value instanceof UserHandle),
"Invalid value for ON_USER_REMOVED: %s", value);
- UserInfo user = (UserInfo) value;
- if (DBG) Slog.d(TAG, "Sending onUserRemoved(): " + user.toFullString());
+ UserHandle user = (UserHandle) value;
+ if (DBG) Slogf.d(TAG, "Sending onUserRemoved(): " + user);
mCarService.onUserRemoved(user);
}
/**
* Sends user life-cycle events to CarService.
*/
- void sendUserLifecycleEvent(@UserLifecycleEventType int eventType, @Nullable TargetUser from,
- @NonNull TargetUser to) {
+ void sendUserLifecycleEvent(@UserLifecycleEventType int eventType, @UserIdInt int fromId,
+ @UserIdInt int toId) {
long now = System.currentTimeMillis();
- int fromId = from == null ? UserHandle.USER_NULL : from.getUserIdentifier();
- int toId = to.getUserIdentifier();
mUserMetrics.onEvent(eventType, now, fromId, toId);
synchronized (mLock) {
if (eventType == USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
- mLastSwitchedUser = to.getUserIdentifier();
- mPreviousUserOfLastSwitchedUser = from.getUserIdentifier();
- mLastUserLifecycle.put(to.getUserIdentifier(), eventType);
+ mLastSwitchedUser = toId;
+ mPreviousUserOfLastSwitchedUser = fromId;
+ mLastUserLifecycle.put(toId, eventType);
} else if (eventType == USER_LIFECYCLE_EVENT_TYPE_STOPPING
|| eventType == USER_LIFECYCLE_EVENT_TYPE_STOPPED) {
- mLastUserLifecycle.delete(to.getUserIdentifier());
+ mLastUserLifecycle.delete(toId);
} else {
- mLastUserLifecycle.put(to.getUserIdentifier(), eventType);
+ mLastUserLifecycle.put(toId, eventType);
}
if (mCarService == null) {
if (DBG) {
- Slog.d(TAG, "CarService null. sendUserLifecycleEvent() deferred for lifecycle"
- + " event " + eventType + " for user " + to);
+ Slogf.d(TAG, "CarService null. sendUserLifecycleEvent() deferred for lifecycle"
+ + " event " + eventType + " for user " + toId);
}
return;
}
}
- sendUserLifecycleEvent(eventType, fromId, toId);
+ sendUserLifecycleEventInternal(eventType, fromId, toId);
}
- private void sendUserLifecycleEvent(@UserLifecycleEventType int eventType,
+ private void sendUserLifecycleEventInternal(@UserLifecycleEventType int eventType,
@UserIdInt int fromId, @UserIdInt int toId) {
if (DBG) {
- Slog.d(TAG, "sendUserLifecycleEvent():" + " eventType=" + eventType + ", fromId="
+ Slogf.d(TAG, "sendUserLifecycleEvent():" + " eventType=" + eventType + ", fromId="
+ fromId + ", toId=" + toId);
}
try {
@@ -414,7 +447,7 @@ final class CarServiceProxy {
mCarService.onUserLifecycleEvent(eventType, fromId, toId);
}
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException from car service", e);
+ Slogf.w(TAG, "RemoteException from car service", e);
handleCarServiceCrash();
}
}
@@ -424,12 +457,12 @@ final class CarServiceProxy {
mCarServiceCrashed = true;
mCarService = null;
}
- Slog.w(TAG, "CarServiceCrashed. No more car service calls before reconnection.");
- mCarServiceHelperService.handleCarServiceCrash();
+ Slogf.w(TAG, "CarServiceCrashed. No more car service calls before reconnection.");
+ mCarServiceHelperServiceUpdatableImpl.handleCarServiceCrash();
}
- private TimingsTraceAndSlog newTimingsTraceAndSlog() {
- return new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+ private TimingsTraceLog newTimingsTraceLog() {
+ return new TimingsTraceLog(TAG, TRACE_TAG_SYSTEM_SERVER);
}
@GuardedBy("mLock")
@@ -440,7 +473,8 @@ final class CarServiceProxy {
@GuardedBy("mLock")
private boolean isServiceCrashedLoggedLocked(@NonNull String operation) {
if (mCarServiceCrashed) {
- Slog.w(TAG, "CarServiceCrashed. " + operation + " will be executed after reconnection");
+ Slogf.w(TAG, "CarServiceCrashed. " + operation + " will be executed after "
+ + "reconnection");
return true;
}
return false;
@@ -450,33 +484,37 @@ final class CarServiceProxy {
* Dump
*/
void dump(IndentingPrintWriter writer) {
+ // Do not change the next line, Used in cts test: testCarServiceHelperServiceDump
writer.println("CarServiceProxy");
writer.increaseIndent();
- writer.printf("mLastSwitchedUser=%s\n", mLastSwitchedUser);
- writer.printf("mLastUserLifecycle:\n");
- int user0Lifecycle = mLastUserLifecycle.get(UserHandle.USER_SYSTEM, 0);
- if (user0Lifecycle != 0) {
- writer.printf("SystemUser Lifecycle Event:%s\n", user0Lifecycle);
- } else {
- writer.println("SystemUser not initialized");
- }
+ synchronized (mLock) {
+ writer.printf("mLastSwitchedUser=%s\n", mLastSwitchedUser);
+ writer.printf("mInitialUser=%s\n", mInitialUser);
+ writer.printf("mLastUserLifecycle:\n");
+ int user0Lifecycle = mLastUserLifecycle.get(USER_SYSTEM, 0);
+ if (user0Lifecycle != 0) {
+ writer.printf("SystemUser Lifecycle Event:%s\n", user0Lifecycle);
+ } else {
+ writer.println("SystemUser not initialized");
+ }
- int lastUserLifecycle = mLastUserLifecycle.get(mLastSwitchedUser, 0);
- if (mLastSwitchedUser != UserHandle.USER_SYSTEM && user0Lifecycle != 0) {
- writer.printf("last user (%s) Lifecycle Event:%s\n",
- mLastSwitchedUser, lastUserLifecycle);
- }
+ int lastUserLifecycle = mLastUserLifecycle.get(mLastSwitchedUser, 0);
+ if (mLastSwitchedUser != USER_SYSTEM && user0Lifecycle != 0) {
+ writer.printf("last user (%s) Lifecycle Event:%s\n",
+ mLastSwitchedUser, lastUserLifecycle);
+ }
- int size = mPendingOperations.size();
- if (size == 0) {
- writer.println("No pending operations");
- } else {
- writer.printf("%d pending operation%s:\n", size, size == 1 ? "" : "s");
- writer.increaseIndent();
- for (int i = 0; i < size; i++) {
- writer.println(mPendingOperations.valueAt(i));
+ int size = mPendingOperations.size();
+ if (size == 0) {
+ writer.println("No pending operations");
+ } else {
+ writer.printf("%d pending operation%s:\n", size, size == 1 ? "" : "s");
+ writer.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ writer.println(mPendingOperations.valueAt(i));
+ }
+ writer.decreaseIndent();
}
- writer.decreaseIndent();
}
writer.decreaseIndent();
dumpUserMetrics(writer);
diff --git a/src/com/android/internal/car/UserMetrics.java b/updatableServices/src/com/android/internal/car/updatable/UserMetrics.java
index ff5fa75..31a4bfd 100644
--- a/src/com/android/internal/car/UserMetrics.java
+++ b/updatableServices/src/com/android/internal/car/updatable/UserMetrics.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.car;
+package com.android.internal.car.updatable;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STARTING;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
@@ -26,13 +26,13 @@ import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVE
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.util.IndentingPrintWriter;
-import android.util.LocalLog;
-import android.util.Slog;
+import android.car.builtin.util.Slogf;
+import android.car.builtin.util.TimeUtils;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.car.internal.common.CommonConstants.UserLifecycleEventType;
+import com.android.car.internal.util.IndentingPrintWriter;
+import com.android.car.internal.util.LocalLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -105,7 +105,7 @@ final class UserMetrics {
onUserStoppedEventLocked(timestampMs, toUserId);
return;
default:
- Slog.w(TAG, "Invalid event: " + eventType);
+ Slogf.w(TAG, "Invalid event: " + eventType);
}
}
}
@@ -131,7 +131,7 @@ final class UserMetrics {
UserStartingMetric existingMetrics = mUserStartingMetrics.get(userId);
if (existingMetrics != null) {
- Slog.w(TAG, "user re-started: " + existingMetrics);
+ Slogf.w(TAG, "user re-started: " + existingMetrics);
finishUserStartingLocked(existingMetrics, /* removeMetric= */ false);
}
@@ -169,7 +169,7 @@ final class UserMetrics {
}
UserStoppingMetric existingMetrics = mUserStoppingMetrics.get(userId);
if (existingMetrics != null) {
- Slog.w(TAG, "user re-stopped: " + existingMetrics);
+ Slogf.w(TAG, "user re-stopped: " + existingMetrics);
finishUserStoppingLocked(existingMetrics, /* removeMetric= */ false);
}
mUserStoppingMetrics.put(userId, new UserStoppingMetric(userId, timestampMs));
@@ -187,14 +187,15 @@ final class UserMetrics {
private <T extends BaseUserMetric> T getExistingMetricsLocked(
@NonNull SparseArray<? extends BaseUserMetric> metrics, @UserIdInt int userId) {
if (metrics == null) {
- Slog.w(TAG, "getExistingMetricsLocked() should not pass null metrics, except on tests");
+ Slogf.w(TAG, "getExistingMetricsLocked() should not pass null metrics, except on "
+ + "tests");
return null;
}
@SuppressWarnings("unchecked")
T metric = (T) metrics.get(userId);
if (metric == null) {
String name = metrics == mUserStartingMetrics ? "starting" : "stopping";
- Slog.w(TAG, "no " + name + " metrics for user " + userId);
+ Slogf.w(TAG, "no " + name + " metrics for user " + userId);
}
return metric;
}
diff --git a/updatableServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatableImpl.java b/updatableServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatableImpl.java
new file mode 100644
index 0000000..d836c03
--- /dev/null
+++ b/updatableServices/src/com/android/server/wm/CarLaunchParamsModifierUpdatableImpl.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
+import android.car.app.CarActivityManager;
+import android.car.builtin.os.UserManagerHelper;
+import android.car.builtin.util.Slogf;
+import android.car.builtin.view.DisplayHelper;
+import android.car.builtin.window.DisplayAreaOrganizerHelper;
+import android.content.ComponentName;
+import android.hardware.display.DisplayManager;
+import android.os.ServiceSpecificException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseIntArray;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of {@link CarLaunchParamsModifierUpdatable}.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class CarLaunchParamsModifierUpdatableImpl
+ implements CarLaunchParamsModifierUpdatable {
+ private static final String TAG = "CAR.LAUNCH";
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final CarLaunchParamsModifierInterface mBuiltin;
+ private final Object mLock = new Object();
+
+ // Always start with USER_SYSTEM as the timing of handleCurrentUserSwitching(USER_SYSTEM) is not
+ // guaranteed to be earler than 1st Activity launch.
+ @GuardedBy("mLock")
+ private int mCurrentDriverUser = UserManagerHelper.USER_SYSTEM;
+
+ // TODO: Switch from tracking displays to tracking display areas instead
+ /**
+ * This one is for holding all passenger (=profile user) displays which are mostly static unless
+ * displays are added / removed. Note that {@link #mDisplayToProfileUserMapping} can be empty
+ * while user is assigned and that cannot always tell if specific display is for driver or not.
+ */
+ @GuardedBy("mLock")
+ private final ArrayList<Integer> mPassengerDisplays = new ArrayList<>();
+
+ /** key: display id, value: profile user id */
+ @GuardedBy("mLock")
+ private final SparseIntArray mDisplayToProfileUserMapping = new SparseIntArray();
+
+ /** key: profile user id, value: display id */
+ @GuardedBy("mLock")
+ private final SparseIntArray mDefaultDisplayForProfileUser = new SparseIntArray();
+
+ @GuardedBy("mLock")
+ private boolean mIsSourcePreferred;
+
+ @GuardedBy("mLock")
+ private List<ComponentName> mSourcePreferredComponents;
+
+ /** key: Activity, value: TaskDisplayAreaWrapper */
+ @GuardedBy("mLock")
+ private final ArrayMap<ComponentName, TaskDisplayAreaWrapper> mPersistentActivities =
+ new ArrayMap<>();
+
+ public CarLaunchParamsModifierUpdatableImpl(CarLaunchParamsModifierInterface builtin) {
+ mBuiltin = builtin;
+ }
+
+ public DisplayManager.DisplayListener getDisplayListener() {
+ return mDisplayListener;
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ // ignore. car service should update whiltelist.
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mPassengerDisplays.remove(Integer.valueOf(displayId));
+ updateProfileUserConfigForDisplayRemovalLocked(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ // ignore
+ }
+ };
+
+ @GuardedBy("mLock")
+ private void updateProfileUserConfigForDisplayRemovalLocked(int displayId) {
+ mDisplayToProfileUserMapping.delete(displayId);
+ int i = mDefaultDisplayForProfileUser.indexOfValue(displayId);
+ if (i >= 0) {
+ mDefaultDisplayForProfileUser.removeAt(i);
+ }
+ }
+
+ /**
+ * Sets {@code sourcePreferred} configuration. When {@code sourcePreferred} is enabled and
+ * there is no pre-assigned display for the Activity, CarLauncherParamsModifier will launch
+ * the Activity in the display of the source. When {@code sourcePreferredComponents} isn't null
+ * the {@code sourcePreferred} is applied for the {@code sourcePreferredComponents} only.
+ *
+ * @param enableSourcePreferred whether to enable sourcePreferred mode
+ * @param sourcePreferredComponents null for all components, or the list of components to apply
+ */
+ public void setSourcePreferredComponents(boolean enableSourcePreferred,
+ @Nullable List<ComponentName> sourcePreferredComponents) {
+ synchronized (mLock) {
+ mIsSourcePreferred = enableSourcePreferred;
+ mSourcePreferredComponents = sourcePreferredComponents;
+ if (mSourcePreferredComponents != null) {
+ Collections.sort(mSourcePreferredComponents);
+ }
+ }
+ }
+
+ /** Notifies user starting. */
+ public void handleUserStarting(int startingUser) {
+ // Do nothing
+ }
+
+ /** Notifies user switching. */
+ public void handleCurrentUserSwitching(@UserIdInt int newUserId) {
+ synchronized (mLock) {
+ mCurrentDriverUser = newUserId;
+ mDefaultDisplayForProfileUser.clear();
+ mDisplayToProfileUserMapping.clear();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void removeUserFromAllowlistsLocked(int userId) {
+ for (int i = mDisplayToProfileUserMapping.size() - 1; i >= 0; i--) {
+ if (mDisplayToProfileUserMapping.valueAt(i) == userId) {
+ mDisplayToProfileUserMapping.removeAt(i);
+ }
+ }
+ mDefaultDisplayForProfileUser.delete(userId);
+ }
+
+ /** Notifies user stopped. */
+ public void handleUserStopped(@UserIdInt int stoppedUser) {
+ // Note that the current user is never stopped. It always takes switching into
+ // non-current user before stopping the user.
+ synchronized (mLock) {
+ removeUserFromAllowlistsLocked(stoppedUser);
+ }
+ }
+
+ /**
+ * Sets display allowlist for the {@code userId}. For passenger user, activity will be always
+ * launched to a display in the allowlist. If requested display is not in the allowlist, the 1st
+ * display in the allowlist will be selected as target display.
+ *
+ * <p>The allowlist is kept only for profile user. Assigning the current user unassigns users
+ * for the given displays.
+ */
+ public void setDisplayAllowListForUser(@UserIdInt int userId, int[] displayIds) {
+ if (DBG) {
+ Slogf.d(TAG, "setDisplayAllowlistForUser userId:%d displays:%s",
+ userId, Arrays.toString(displayIds));
+ }
+ synchronized (mLock) {
+ for (int displayId : displayIds) {
+ if (!mPassengerDisplays.contains(displayId)) {
+ Slogf.w(TAG, "setDisplayAllowlistForUser called with display:%d"
+ + " not in passenger display list:%s", displayId, mPassengerDisplays);
+ continue;
+ }
+ if (userId == mCurrentDriverUser) {
+ mDisplayToProfileUserMapping.delete(displayId);
+ } else {
+ mDisplayToProfileUserMapping.put(displayId, userId);
+ }
+ // now the display cannot be a default display for other user
+ int i = mDefaultDisplayForProfileUser.indexOfValue(displayId);
+ if (i >= 0) {
+ mDefaultDisplayForProfileUser.removeAt(i);
+ }
+ }
+ if (displayIds.length > 0) {
+ mDefaultDisplayForProfileUser.put(userId, displayIds[0]);
+ } else {
+ removeUserFromAllowlistsLocked(userId);
+ }
+ }
+ }
+
+ /**
+ * Sets displays assigned to passenger. All other displays will be treated as assigned to
+ * driver.
+ *
+ * <p>The 1st display in the array will be considered as a default display to assign
+ * for any non-driver user if there is no display assigned for the user. </p>
+ */
+ public void setPassengerDisplays(int[] displayIdsForPassenger) {
+ if (DBG) {
+ Slogf.d(TAG, "setPassengerDisplays displays:%s",
+ Arrays.toString(displayIdsForPassenger));
+ }
+ synchronized (mLock) {
+ for (int id : displayIdsForPassenger) {
+ mPassengerDisplays.remove(Integer.valueOf(id));
+ }
+ // handle removed displays
+ for (int i = 0; i < mPassengerDisplays.size(); i++) {
+ int displayId = mPassengerDisplays.get(i);
+ updateProfileUserConfigForDisplayRemovalLocked(displayId);
+ }
+ mPassengerDisplays.clear();
+ mPassengerDisplays.ensureCapacity(displayIdsForPassenger.length);
+ for (int id : displayIdsForPassenger) {
+ mPassengerDisplays.add(id);
+ }
+ }
+ }
+
+ /**
+ * Calculates {@code outParams} based on the given arguments.
+ * See {@code LaunchParamsController.LaunchParamsModifier.onCalculate()} for the detail.
+ */
+ public int calculate(CalculateParams params) {
+ TaskWrapper task = params.getTask();
+ ActivityRecordWrapper activity = params.getActivity();
+ ActivityRecordWrapper source = params.getSource();
+ ActivityOptionsWrapper options = params.getOptions();
+ RequestWrapper request = params.getRequest();
+ LaunchParamsWrapper currentParams = params.getCurrentParams();
+ LaunchParamsWrapper outParams = params.getOutParams();
+
+ int userId;
+ if (task != null) {
+ userId = task.getUserId();
+ } else if (activity != null) {
+ userId = activity.getUserId();
+ } else {
+ Slogf.w(TAG, "onCalculate, cannot decide user");
+ return LaunchParamsWrapper.RESULT_SKIP;
+ }
+ // DisplayArea where user wants to launch the Activity.
+ TaskDisplayAreaWrapper originalDisplayArea = currentParams.getPreferredTaskDisplayArea();
+ // DisplayArea where CarLaunchParamsModifier targets to launch the Activity.
+ TaskDisplayAreaWrapper targetDisplayArea = null;
+ if (DBG) {
+ Slogf.d(TAG, "onCalculate, userId:%d original displayArea:%s ActivityOptions:%s",
+ userId, originalDisplayArea, options);
+ }
+ ComponentName activityName = activity.getComponentName();
+ decision:
+ synchronized (mLock) {
+ // If originalDisplayArea is set, respect that before ActivityOptions check.
+ if (originalDisplayArea == null) {
+ if (options != null) {
+ originalDisplayArea = options.getLaunchTaskDisplayArea();
+ if (originalDisplayArea == null) {
+ originalDisplayArea = mBuiltin.getDefaultTaskDisplayAreaOnDisplay(
+ options.getOptions().getLaunchDisplayId());
+ }
+ }
+ }
+ if (mPersistentActivities.containsKey(activityName)) {
+ targetDisplayArea = mPersistentActivities.get(activityName);
+ } else if (originalDisplayArea == null // No specified DA to launch the Activity
+ && mIsSourcePreferred && source != null
+ && (mSourcePreferredComponents == null || Collections.binarySearch(
+ mSourcePreferredComponents, activityName) >= 0)) {
+ targetDisplayArea = source.isNoDisplay() ? source.getHandoverTaskDisplayArea()
+ : source.getDisplayArea();
+ } else if (originalDisplayArea == null
+ && task == null // launching as a new task
+ && source != null && !source.isDisplayTrusted()
+ && !source.allowingEmbedded()) {
+ if (DBG) {
+ Slogf.d(TAG, "Disallow launch on virtual display for not-embedded activity.");
+ }
+ targetDisplayArea = mBuiltin.getDefaultTaskDisplayAreaOnDisplay(
+ Display.DEFAULT_DISPLAY);
+ }
+ if (userId == mCurrentDriverUser) {
+ // Respect the existing DisplayArea.
+ break decision;
+ }
+ if (userId == UserManagerHelper.USER_SYSTEM) {
+ // This will be only allowed if it has FLAG_SHOW_FOR_ALL_USERS.
+ // The flag is not immediately accessible here so skip the check.
+ // But other WM policy will enforce it.
+ break decision;
+ }
+ // Now user is a passenger.
+ if (mPassengerDisplays.isEmpty()) {
+ // No displays for passengers. This could be old user and do not do anything.
+ break decision;
+ }
+ if (targetDisplayArea == null) {
+ if (originalDisplayArea != null) {
+ targetDisplayArea = originalDisplayArea;
+ } else {
+ targetDisplayArea = mBuiltin.getDefaultTaskDisplayAreaOnDisplay(
+ Display.DEFAULT_DISPLAY);
+ }
+ }
+ Display display = targetDisplayArea.getDisplay();
+ if ((display.getFlags() & Display.FLAG_PRIVATE) != 0) {
+ // private display should follow its own restriction rule.
+ break decision;
+ }
+ if (DisplayHelper.getType(display) == DisplayHelper.TYPE_VIRTUAL) {
+ // TODO(b/132903422) : We need to update this after the bug is resolved.
+ // For now, don't change anything.
+ break decision;
+ }
+ int userForDisplay = mDisplayToProfileUserMapping.get(display.getDisplayId(),
+ UserManagerHelper.USER_NULL);
+ if (userForDisplay == userId) {
+ break decision;
+ }
+ targetDisplayArea = getAlternativeDisplayAreaForPassengerLocked(
+ userId, activity, request);
+ }
+ if (targetDisplayArea != null && originalDisplayArea != targetDisplayArea) {
+ Slogf.i(TAG, "Changed launching display, user:%d requested display area:%s"
+ + " target display area:", userId, originalDisplayArea, targetDisplayArea);
+ outParams.setPreferredTaskDisplayArea(targetDisplayArea);
+ return LaunchParamsWrapper.RESULT_DONE;
+ } else {
+ return LaunchParamsWrapper.RESULT_SKIP;
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private TaskDisplayAreaWrapper getAlternativeDisplayAreaForPassengerLocked(int userId,
+ @NonNull ActivityRecordWrapper activtyRecord, @Nullable RequestWrapper request) {
+ List<TaskDisplayAreaWrapper> fallbacks = mBuiltin.getFallbackDisplayAreasForActivity(
+ activtyRecord, request);
+ for (int i = 0, size = fallbacks.size(); i < size; ++i) {
+ TaskDisplayAreaWrapper fallbackTda = fallbacks.get(i);
+ int userForDisplay = getUserIdForDisplayLocked(fallbackTda.getDisplay().getDisplayId());
+ if (userForDisplay == userId) {
+ return fallbackTda;
+ }
+ }
+ return fallbackDisplayAreaForUserLocked(userId);
+ }
+
+ /**
+ * Returns {@code userId} who is allowed to use the given {@code displayId}, or
+ * {@code UserHandle.USER_NULL} if the display doesn't exist in the mapping.
+ */
+ @GuardedBy("mLock")
+ private int getUserIdForDisplayLocked(int displayId) {
+ return mDisplayToProfileUserMapping.get(displayId, UserManagerHelper.USER_NULL);
+ }
+
+ /**
+ * Return a {@link TaskDisplayAreaWrapper} that can be used if a source display area is
+ * not found. First check the default display for the user. If it is absent select
+ * the first passenger display if present. If both are absent return {@code null}
+ *
+ * @param userId ID of the active user
+ * @return {@link TaskDisplayAreaWrapper} that is recommended when a display area is
+ * not specified
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private TaskDisplayAreaWrapper fallbackDisplayAreaForUserLocked(@UserIdInt int userId) {
+ int displayIdForUserProfile = mDefaultDisplayForProfileUser.get(userId,
+ Display.INVALID_DISPLAY);
+ if (displayIdForUserProfile != Display.INVALID_DISPLAY) {
+ int displayId = mDefaultDisplayForProfileUser.get(userId);
+ return mBuiltin.getDefaultTaskDisplayAreaOnDisplay(displayId);
+ }
+ if (!mPassengerDisplays.isEmpty()) {
+ int displayId = mPassengerDisplays.get(0);
+ return mBuiltin.getDefaultTaskDisplayAreaOnDisplay(displayId);
+ }
+ return null;
+ }
+
+ /**
+ * See {@link CarActivityManager#setPersistentActivity(android.content.ComponentName,int, int)}
+ */
+ public int setPersistentActivity(ComponentName activity, int displayId, int featureId) {
+ if (DBG) {
+ Slogf.d(TAG, "setPersistentActivity: activity=%s, displayId=%d, featureId=%d",
+ activity, displayId, featureId);
+ }
+ if (featureId == DisplayAreaOrganizerHelper.FEATURE_UNDEFINED) {
+ synchronized (mLock) {
+ TaskDisplayAreaWrapper removed = mPersistentActivities.remove(activity);
+ if (removed == null) {
+ throw new ServiceSpecificException(
+ CarActivityManager.ERROR_CODE_ACTIVITY_NOT_FOUND,
+ "Failed to remove " + activity.toShortString());
+ }
+ return CarActivityManager.RESULT_SUCCESS;
+ }
+ }
+ TaskDisplayAreaWrapper tda = mBuiltin.findTaskDisplayArea(displayId, featureId);
+ if (tda == null) {
+ throw new IllegalArgumentException("Unknown display=" + displayId
+ + " or feature=" + featureId);
+ }
+ synchronized (mLock) {
+ mPersistentActivities.put(activity, tda);
+ }
+ return CarActivityManager.RESULT_SUCCESS;
+ }
+} \ No newline at end of file
diff --git a/updatableServices/tests/Android.bp b/updatableServices/tests/Android.bp
new file mode 100644
index 0000000..7d9e971
--- /dev/null
+++ b/updatableServices/tests/Android.bp
@@ -0,0 +1,50 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+}
+
+android_test {
+ name: "FrameworkOptCarServicesUpdatableTest",
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ platform_apis: true,
+
+ certificate: "platform",
+
+ optimize: {
+ enabled: false,
+ },
+
+ libs: [
+ "android.car",
+ "android.car.builtin",
+ "android.test.runner",
+ "android.test.base",
+ "android.hardware.automotive.vehicle-V2.0-java",
+ ],
+
+ static_libs: [
+ "android.car.test.utils",
+ "android.car.watchdoglib",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "car-frameworks-service.impl",
+ "car-frameworks-service-module",
+ "mockito-target-extended-minus-junit4",
+ "services.core",
+ "testng",
+ "truth-prebuilt",
+ ],
+
+ // mockito-target-extended dependencies
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+}
diff --git a/updatableServices/tests/AndroidManifest.xml b/updatableServices/tests/AndroidManifest.xml
new file mode 100644
index 0000000..2a3e07f
--- /dev/null
+++ b/updatableServices/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="com.android.internal.car.updatable"
+ android:sharedUserId="android.uid.system" >
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.car.updatable"
+ android:label="Unit Tests for Car Framework Updatable Services"/>
+
+ <application android:label="FrameworkOptCarServicesUpdatableTest"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/updatableServices/tests/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImplTest.java b/updatableServices/tests/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImplTest.java
new file mode 100644
index 0000000..6e5231b
--- /dev/null
+++ b/updatableServices/tests/src/com/android/internal/car/updatable/CarServiceHelperServiceUpdatableImplTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.car.updatable;
+
+import static com.android.car.internal.common.CommonConstants.CAR_SERVICE_INTERFACE;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+
+import android.car.ICar;
+import android.car.builtin.os.UserManagerHelper;
+import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.car.CarServiceHelperInterface;
+import com.android.server.wm.CarLaunchParamsModifierInterface;
+
+import java.util.function.BiConsumer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * This class contains unit tests for the {@link CarServiceHelperServiceUpdatableImpl}.
+ */
+@RunWith(AndroidJUnit4.class)
+public final class CarServiceHelperServiceUpdatableImplTest
+ extends AbstractExtendedMockitoTestCase {
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private CarServiceProxy mCarServiceProxy;
+ @Mock
+ private CarServiceHelperInterface mCarServiceHelperInterface;
+ @Mock
+ private CarLaunchParamsModifierInterface mCarLaunchParamsModifierInterface;
+ @Mock
+ private ICar mICarBinder;
+ @Mock
+ private IBinder mIBinder;
+
+ private CarServiceHelperServiceUpdatableImpl mCarServiceHelperServiceUpdatableImpl;
+
+ public CarServiceHelperServiceUpdatableImplTest() {
+ super(CarServiceHelperServiceUpdatableImpl.TAG);
+ }
+
+ @Before
+ public void setTestFixtures() {
+ mCarServiceHelperServiceUpdatableImpl = new CarServiceHelperServiceUpdatableImpl(
+ mMockContext,
+ mCarServiceHelperInterface,
+ mCarLaunchParamsModifierInterface,
+ mCarServiceProxy);
+ }
+
+ @Test
+ public void testCarServiceLaunched() throws Exception {
+ mockSystemContext();
+ mockBindService();
+
+ mCarServiceHelperServiceUpdatableImpl.onStart();
+
+ verifyBindService();
+ }
+
+ @Test
+ public void testHandleCarServiceConnection() throws Exception {
+ mockICarBinder();
+
+ mCarServiceHelperServiceUpdatableImpl.handleCarServiceConnection(mIBinder);
+
+ verify(mICarBinder).setSystemServerConnections(any(), any());
+ }
+
+ @Test
+ public void testHandleCarServiceCrash() throws Exception {
+ mockICarBinder();
+ doThrow(new RemoteException()).when(mICarBinder).setSystemServerConnections(any(), any());
+
+ mCarServiceHelperServiceUpdatableImpl.handleCarServiceConnection(mIBinder);
+
+ verify(mCarServiceHelperInterface).dumpServiceStacks();
+ }
+
+ @Test
+ public void testOnUserRemoved() throws Exception {
+ UserHandle user = UserHandle.of(101);
+ mCarServiceHelperServiceUpdatableImpl.onUserRemoved(user);
+
+ verify(mCarServiceProxy).onUserRemoved(user);
+ }
+
+ @Test
+ public void testOnFactoryReset() throws Exception {
+ BiConsumer<Integer, Bundle> callback = (x, y) -> {};
+ mCarServiceHelperServiceUpdatableImpl.onFactoryReset(callback);
+
+ verify(mCarServiceProxy).onFactoryReset(any());
+ }
+
+ @Test
+ public void testInitBootUser() throws Exception {
+ mCarServiceHelperServiceUpdatableImpl.initBootUser();
+
+ verify(mCarServiceProxy).initBootUser();
+ }
+
+ @Test
+ public void testSendUserLifecycleEvent_nullFromUser() throws Exception {
+ int eventType = 1;
+ UserHandle userFrom = null;
+ UserHandle userTo = UserHandle.SYSTEM;
+
+ mCarServiceHelperServiceUpdatableImpl.sendUserLifecycleEvent(eventType, userFrom, userTo);
+
+ verify(mCarServiceProxy).sendUserLifecycleEvent(eventType, UserManagerHelper.USER_NULL,
+ userTo.getIdentifier());
+ }
+
+ @Test
+ public void testSendUserLifecycleEvent() throws Exception {
+ int eventType = 1;
+ UserHandle userFrom = UserHandle.SYSTEM;
+ int userId = 101;
+ UserHandle userTo = UserHandle.of(userId);
+
+ mCarServiceHelperServiceUpdatableImpl.sendUserLifecycleEvent(eventType, userFrom, userTo);
+
+ verify(mCarServiceProxy).sendUserLifecycleEvent(eventType, userFrom.getIdentifier(),
+ userTo.getIdentifier());
+ }
+
+ private void mockICarBinder() {
+ when(ICar.Stub.asInterface(mIBinder)).thenReturn(mICarBinder);
+ }
+
+ private void mockSystemContext() {
+ when(mMockContext.createContextAsUser(UserHandle.SYSTEM, /* flags= */ 0))
+ .thenReturn(mMockContext);
+ }
+
+ private void mockBindService() {
+ when(mMockContext.bindService(any(), eq(Context.BIND_AUTO_CREATE), any(), any()))
+ .thenReturn(true);
+ }
+
+ private void verifyBindService() throws Exception {
+ verify(mMockContext).bindService(
+ argThat(intent -> intent.getAction().equals(CAR_SERVICE_INTERFACE)),
+ eq(Context.BIND_AUTO_CREATE), any(), any());
+ }
+}
diff --git a/tests/src/com/android/internal/car/CarServiceProxyTest.java b/updatableServices/tests/src/com/android/internal/car/updatable/CarServiceProxyTest.java
index 74bc8b0..714cf55 100644
--- a/tests/src/com/android/internal/car/CarServiceProxyTest.java
+++ b/updatableServices/tests/src/com/android/internal/car/updatable/CarServiceProxyTest.java
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
-package com.android.internal.car;
+package com.android.internal.car.updatable;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
@@ -24,13 +22,13 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.car.ICarResultReceiver;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.car.test.util.UserTestingHelper.UserInfoBuilder;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import com.android.car.internal.ICarSystemServerClient;
-import com.android.internal.os.IResultReceiver;
import com.android.server.SystemService.TargetUser;
import org.junit.Before;
@@ -40,15 +38,15 @@ import org.mockito.Mock;
public class CarServiceProxyTest extends AbstractExtendedMockitoTestCase {
@Mock
- private CarServiceHelperService mCarServiceHelperService;
+ private CarServiceHelperServiceUpdatableImpl mCarServiceHelperServiceUpdatableImpl;
@Mock
private ICarSystemServerClient mCarService;
@Mock
- private IResultReceiver mFactoryResetCallback1;
+ private ICarResultReceiver mFactoryResetCallback1;
@Mock
- private IResultReceiver mFactoryResetCallback2;
+ private ICarResultReceiver mFactoryResetCallback2;
private final TargetUser mFromUser = new TargetUser(new UserInfo(101, "fromUser", 0));
private final TargetUser mToUser = new TargetUser(new UserInfo(102, "toUser", 0));
@@ -59,9 +57,13 @@ public class CarServiceProxyTest extends AbstractExtendedMockitoTestCase {
private CarServiceProxy mCarServiceProxy;
+ public CarServiceProxyTest() {
+ super(CarServiceProxy.TAG);
+ }
+
@Before
public void setUpMocks() {
- mCarServiceProxy = new CarServiceProxy(mCarServiceHelperService);
+ mCarServiceProxy = new CarServiceProxy(mCarServiceHelperServiceUpdatableImpl);
}
@Test
@@ -170,16 +172,17 @@ public class CarServiceProxyTest extends AbstractExtendedMockitoTestCase {
}
private void callSendLifecycleEvent(int eventType) {
- mCarServiceProxy.sendUserLifecycleEvent(eventType, mFromUser, mToUser);
+ mCarServiceProxy.sendUserLifecycleEvent(eventType, mFromUser.getUserIdentifier(),
+ mToUser.getUserIdentifier());
}
private void callOnUserRemoved() {
- mCarServiceProxy.onUserRemoved(mRemovedUser1);
- mCarServiceProxy.onUserRemoved(mRemovedUser2);
- mCarServiceProxy.onUserRemoved(mRemovedUser3);
+ mCarServiceProxy.onUserRemoved(mRemovedUser1.getUserHandle());
+ mCarServiceProxy.onUserRemoved(mRemovedUser2.getUserHandle());
+ mCarServiceProxy.onUserRemoved(mRemovedUser3.getUserHandle());
}
- private void callOnFactoryReset(IResultReceiver callback) {
+ private void callOnFactoryReset(ICarResultReceiver callback) {
mCarServiceProxy.onFactoryReset(callback);
}
@@ -201,20 +204,20 @@ public class CarServiceProxyTest extends AbstractExtendedMockitoTestCase {
}
private void verifyOnUserRemovedCalled() throws RemoteException {
- verify(mCarService).onUserRemoved(mRemovedUser1);
- verify(mCarService).onUserRemoved(mRemovedUser2);
- verify(mCarService).onUserRemoved(mRemovedUser3);
+ verify(mCarService).onUserRemoved(mRemovedUser1.getUserHandle());
+ verify(mCarService).onUserRemoved(mRemovedUser2.getUserHandle());
+ verify(mCarService).onUserRemoved(mRemovedUser3.getUserHandle());
}
private void verifyOnUserRemovedNeverCalled() throws RemoteException {
verify(mCarService, never()).onUserRemoved(any());
}
- private void verifyOnFactoryResetCalled(IResultReceiver callback) throws RemoteException {
+ private void verifyOnFactoryResetCalled(ICarResultReceiver callback) throws RemoteException {
verify(mCarService).onFactoryReset(callback);
}
- private void verifyOnFactoryResetNotCalled(IResultReceiver callback) throws RemoteException {
+ private void verifyOnFactoryResetNotCalled(ICarResultReceiver callback) throws RemoteException {
verify(mCarService, never()).onFactoryReset(callback);
}
diff --git a/tests/src/com/android/internal/car/UserMetricsTest.java b/updatableServices/tests/src/com/android/internal/car/updatable/UserMetricsTest.java
index 67ed9f9..b778de4 100644
--- a/tests/src/com/android/internal/car/UserMetricsTest.java
+++ b/updatableServices/tests/src/com/android/internal/car/updatable/UserMetricsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.car;
+package com.android.internal.car.updatable;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STARTING;
import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
@@ -31,8 +31,8 @@ import android.annotation.UserIdInt;
import android.os.SystemClock;
import android.util.SparseArray;
-import com.android.internal.car.UserMetrics.UserStartingMetric;
-import com.android.internal.car.UserMetrics.UserStoppingMetric;
+import com.android.internal.car.updatable.UserMetrics.UserStartingMetric;
+import com.android.internal.car.updatable.UserMetrics.UserStoppingMetric;
import org.junit.Test;
diff --git a/tests/src/com/android/server/wm/CarLaunchParamsModifierTest.java b/updatableServices/tests/src/com/android/server/wm/CarLaunchParamsModifierUpdatableTest.java
index d641cff..13dc733 100644
--- a/tests/src/com/android/server/wm/CarLaunchParamsModifierTest.java
+++ b/updatableServices/tests/src/com/android/server/wm/CarLaunchParamsModifierUpdatableTest.java
@@ -25,33 +25,46 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertThrows;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+import android.car.app.CarActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.view.Display;
import android.view.SurfaceControl;
+import android.window.DisplayAreaOrganizer;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.policy.AttributeCache;
import com.android.server.LocalServices;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.input.InputManagerService;
+import com.android.server.policy.WindowManagerPolicy;
import org.junit.After;
import org.junit.Before;
@@ -62,24 +75,30 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.Arrays;
+import java.util.function.Function;
/**
* Tests for {@link CarLaunchParamsModifier}
* Build/Install/Run:
- * atest CarServicesTest:CarLaunchParamsModifierTest
+ * atest FrameworkOptCarServicesUpdatableTest:CarLaunchParamsModifierUpdatableTest
*/
@RunWith(AndroidJUnit4.class)
-public class CarLaunchParamsModifierTest {
+public class CarLaunchParamsModifierUpdatableTest {
private static final int PASSENGER_DISPLAY_ID_10 = 10;
private static final int PASSENGER_DISPLAY_ID_11 = 11;
private static final int VIRTUAL_DISPLAY_ID_2 = 2;
+ private static final int FEATURE_MAP_ID = 1111;
private MockitoSession mMockingSession;
private CarLaunchParamsModifier mModifier;
+ private CarLaunchParamsModifierUpdatableImpl mUpdatable;
+ private CarLaunchParamsModifierInterface mBuiltin;
- @Mock
private Context mContext;
+ private WindowManagerService mWindowManagerService;
+ private final WindowManagerGlobalLock mWindowManagerGlobalLock = new WindowManagerGlobalLock();
+
@Mock
private DisplayManager mDisplayManager;
@Mock
@@ -89,13 +108,15 @@ public class CarLaunchParamsModifierTest {
@Mock
private RecentTasks mRecentTasks;
@Mock
- private WindowManagerService mWindowManagerService;
- @Mock
private ColorDisplayService.ColorDisplayServiceInternal mColorDisplayServiceInternal;
@Mock
private RootWindowContainer mRootWindowContainer;
@Mock
private LaunchParamsController mLaunchParamsController;
+ @Mock
+ private PackageConfigPersister mPackageConfigPersister;
+ @Mock
+ private InputManagerService mInputManagerService;
@Mock
private Display mDisplay0ForDriver;
@@ -117,6 +138,7 @@ public class CarLaunchParamsModifierTest {
private Display mDisplay2Virtual;
@Mock
private TaskDisplayArea mDisplayArea2Virtual;
+ private TaskDisplayArea mMapTaskDisplayArea;
// All mocks from here before CarLaunchParamsModifier are arguments for
// LaunchParamsModifier.onCalculate() call.
@@ -145,6 +167,7 @@ public class CarLaunchParamsModifierTest {
// Return the same id as the display for simplicity
DisplayContent dc = mock(DisplayContent.class);
defaultTaskDisplayArea.mDisplayContent = dc;
+ when(defaultTaskDisplayArea.getDisplayContent()).thenReturn(dc);
when(mRootWindowContainer.getDisplayContentOrCreate(displayId)).thenReturn(dc);
when(dc.getDisplay()).thenReturn(display);
when(dc.getDefaultTaskDisplayArea()).thenReturn(defaultTaskDisplayArea);
@@ -158,21 +181,46 @@ public class CarLaunchParamsModifierTest {
.mockStatic(ActivityTaskManager.class)
.strictness(Strictness.LENIENT)
.startMocking();
- when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManager);
+ mContext = getInstrumentation().getTargetContext();
+ spyOn(mContext);
+ doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
+
doReturn(mActivityTaskManagerService).when(() -> ActivityTaskManager.getService());
mActivityTaskManagerService.mTaskSupervisor = mActivityTaskSupervisor;
when(mActivityTaskSupervisor.getLaunchParamsController()).thenReturn(
mLaunchParamsController);
+ mActivityTaskManagerService.mSupportsMultiDisplay = true;
mActivityTaskManagerService.mRootWindowContainer = mRootWindowContainer;
- mActivityTaskManagerService.mWindowManager = mWindowManagerService;
+ mActivityTaskManagerService.mPackageConfigPersister = mPackageConfigPersister;
+ mActivityTaskManagerService.mWindowOrganizerController =
+ new WindowOrganizerController(mActivityTaskManagerService);
+ when(mActivityTaskManagerService.getTransitionController()).thenCallRealMethod();
when(mActivityTaskManagerService.getRecentTasks()).thenReturn(mRecentTasks);
- mWindowManagerService.mTransactionFactory = () -> new SurfaceControl.Transaction();
+ when(mActivityTaskManagerService.getGlobalLock()).thenReturn(mWindowManagerGlobalLock);
+
+ mWindowManagerService = WindowManagerService.main(
+ mContext, mInputManagerService, /* showBootMsgs= */ false, /* onlyCore= */ false,
+ /* policy= */ null, mActivityTaskManagerService,
+ /* displayWindowSettingsProvider= */ null, () -> new SurfaceControl.Transaction(),
+ /* surfaceFactory= */ null, /* surfaceControlFactory= */ null);
+ mActivityTaskManagerService.mWindowManager = mWindowManagerService;
+ mRootWindowContainer.mWindowManager = mWindowManagerService;
+
AttributeCache.init(getInstrumentation().getTargetContext());
LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
mColorDisplayServiceInternal);
when(mActivityOptions.getLaunchDisplayId()).thenReturn(INVALID_DISPLAY);
mockDisplay(mDisplay0ForDriver, mDisplayArea0ForDriver, DEFAULT_DISPLAY,
FLAG_TRUSTED, /* type= */ 0);
+ DisplayContent defaultDC = mRootWindowContainer.getDisplayContentOrCreate(DEFAULT_DISPLAY);
+ mMapTaskDisplayArea = new TaskDisplayArea(
+ defaultDC, mWindowManagerService, "MapTDA", FEATURE_MAP_ID);
+ doAnswer((invocation) -> {
+ Function<TaskDisplayArea, TaskDisplayArea> callback = invocation.getArgument(0);
+ return callback.apply(mMapTaskDisplayArea);
+ }).when(defaultDC).getItemFromTaskDisplayAreas(any());
+ when(mActivityRecordSource.getDisplayContent()).thenReturn(defaultDC);
+
mockDisplay(mDisplay10ForPassenger, mDisplayArea10ForPassenger, PASSENGER_DISPLAY_ID_10,
FLAG_TRUSTED, /* type= */ 0);
mockDisplay(mDisplay11ForPassenger, mDisplayArea11ForPassenger, PASSENGER_DISPLAY_ID_11,
@@ -181,23 +229,26 @@ public class CarLaunchParamsModifierTest {
FLAG_TRUSTED | FLAG_PRIVATE, /* type= */ 0);
mockDisplay(mDisplay2Virtual, mDisplayArea2Virtual, VIRTUAL_DISPLAY_ID_2,
FLAG_PRIVATE, /* type= */ 0);
- DisplayContent defaultDc = mRootWindowContainer.getDisplayContentOrCreate(DEFAULT_DISPLAY);
- when(mActivityRecordSource.getDisplayContent()).thenReturn(defaultDc);
mModifier = new CarLaunchParamsModifier(mContext);
+ mBuiltin = mModifier.getBuiltinInterface();
+ mUpdatable = new CarLaunchParamsModifierUpdatableImpl(mBuiltin);
+ mModifier.setUpdatable(mUpdatable);
mModifier.init();
}
@After
public void tearDown() {
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerPolicy.class);
LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
mMockingSession.finishMocking();
}
private void assertDisplayIsAllowed(@UserIdInt int userId, Display display) {
mTask.mUserId = userId;
- mCurrentParams.mPreferredTaskDisplayArea = mModifier
- .getDefaultTaskDisplayAreaOnDisplay(display.getDisplayId());
+ mCurrentParams.mPreferredTaskDisplayArea = mBuiltin.getDefaultTaskDisplayAreaOnDisplay(
+ display.getDisplayId()).getTaskDisplayArea();
assertThat(mModifier.onCalculate(mTask, mWindowLayout, mActivityRecordActivity,
mActivityRecordSource, mActivityOptions, null /* request */, 0, mCurrentParams,
mOutParams))
@@ -208,10 +259,10 @@ public class CarLaunchParamsModifierTest {
Display displayAssigned) {
assertThat(displayRequested.getDisplayId()).isNotEqualTo(displayAssigned.getDisplayId());
mTask.mUserId = userId;
- TaskDisplayArea requestedTaskDisplayArea = mModifier
- .getDefaultTaskDisplayAreaOnDisplay(displayRequested.getDisplayId());
- TaskDisplayArea assignedTaskDisplayArea = mModifier
- .getDefaultTaskDisplayAreaOnDisplay(displayAssigned.getDisplayId());
+ TaskDisplayArea requestedTaskDisplayArea = mBuiltin.getDefaultTaskDisplayAreaOnDisplay(
+ displayRequested.getDisplayId()).getTaskDisplayArea();
+ TaskDisplayArea assignedTaskDisplayArea = mBuiltin.getDefaultTaskDisplayAreaOnDisplay(
+ displayAssigned.getDisplayId()).getTaskDisplayArea();
mCurrentParams.mPreferredTaskDisplayArea = requestedTaskDisplayArea;
assertThat(mModifier.onCalculate(mTask, mWindowLayout, mActivityRecordActivity,
mActivityRecordSource, mActivityOptions, null /* request */, 0, mCurrentParams,
@@ -255,9 +306,14 @@ public class CarLaunchParamsModifierTest {
return new ActivityRecord.Builder(mActivityTaskManagerService)
.setIntent(intent)
.setActivityInfo(info)
+ .setConfiguration(new Configuration())
.build();
}
+ private ActivityRecord buildActivityRecord(ComponentName componentName) {
+ return buildActivityRecord(componentName.getPackageName(), componentName.getClassName());
+ }
+
@Test
public void testNoPolicySet() {
final int randomUserId = 1000;
@@ -280,7 +336,7 @@ public class CarLaunchParamsModifierTest {
@Test
public void testAllowAllForDriverDuringBoot() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay10ForPassenger.getDisplayId()});
// USER_SYSTEM should be allowed always
@@ -289,7 +345,7 @@ public class CarLaunchParamsModifierTest {
@Test
public void testAllowAllForDriverAfterUserSwitching() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay10ForPassenger.getDisplayId()});
final int driver1 = 10;
@@ -305,11 +361,11 @@ public class CarLaunchParamsModifierTest {
@Test
public void testPassengerAllowed() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
final int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(passengerUserId,
+ mUpdatable.setDisplayAllowListForUser(passengerUserId,
new int[]{mDisplay10ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId, mDisplay10ForPassenger);
@@ -317,17 +373,17 @@ public class CarLaunchParamsModifierTest {
@Test
public void testPassengerChange() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
int passengerUserId1 = 100;
- mModifier.setDisplayAllowListForUser(passengerUserId1,
+ mUpdatable.setDisplayAllowListForUser(passengerUserId1,
new int[]{mDisplay11ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId1, mDisplay11ForPassenger);
int passengerUserId2 = 101;
- mModifier.setDisplayAllowListForUser(passengerUserId2,
+ mUpdatable.setDisplayAllowListForUser(passengerUserId2,
new int[]{mDisplay11ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId2, mDisplay11ForPassenger);
@@ -337,11 +393,11 @@ public class CarLaunchParamsModifierTest {
@Test
public void testPassengerNotAllowed() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
final int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(
+ mUpdatable.setDisplayAllowListForUser(
passengerUserId, new int[]{mDisplay10ForPassenger.getDisplayId()});
assertDisplayIsReassigned(passengerUserId, mDisplay0ForDriver, mDisplay10ForPassenger);
@@ -350,11 +406,11 @@ public class CarLaunchParamsModifierTest {
@Test
public void testPassengerNotAllowedAfterUserSwitch() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(
+ mUpdatable.setDisplayAllowListForUser(
passengerUserId, new int[]{mDisplay11ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId, mDisplay11ForPassenger);
@@ -366,15 +422,15 @@ public class CarLaunchParamsModifierTest {
@Test
public void testPassengerNotAllowedAfterAssigningCurrentUser() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(
+ mUpdatable.setDisplayAllowListForUser(
passengerUserId, new int[]{mDisplay11ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId, mDisplay11ForPassenger);
- mModifier.setDisplayAllowListForUser(
+ mUpdatable.setDisplayAllowListForUser(
UserHandle.USER_SYSTEM, new int[]{mDisplay11ForPassenger.getDisplayId()});
assertDisplayIsReassigned(passengerUserId, mDisplay0ForDriver, mDisplay10ForPassenger);
@@ -383,18 +439,18 @@ public class CarLaunchParamsModifierTest {
@Test
public void testPassengerDisplayRemoved() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
final int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(passengerUserId,
+ mUpdatable.setDisplayAllowListForUser(passengerUserId,
new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId, mDisplay10ForPassenger);
assertDisplayIsAllowed(passengerUserId, mDisplay11ForPassenger);
- mModifier.mDisplayListener.onDisplayRemoved(mDisplay11ForPassenger.getDisplayId());
+ mUpdatable.getDisplayListener().onDisplayRemoved(mDisplay11ForPassenger.getDisplayId());
assertDisplayIsAllowed(passengerUserId, mDisplay10ForPassenger);
assertDisplayIsReassigned(passengerUserId, mDisplay11ForPassenger, mDisplay10ForPassenger);
@@ -402,18 +458,18 @@ public class CarLaunchParamsModifierTest {
@Test
public void testPassengerDisplayRemovedFromSetPassengerDisplays() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
final int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(passengerUserId,
+ mUpdatable.setDisplayAllowListForUser(passengerUserId,
new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId, mDisplay10ForPassenger);
assertDisplayIsAllowed(passengerUserId, mDisplay11ForPassenger);
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId()});
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId()});
assertDisplayIsAllowed(passengerUserId, mDisplay10ForPassenger);
assertDisplayIsReassigned(passengerUserId, mDisplay11ForPassenger, mDisplay10ForPassenger);
@@ -421,11 +477,11 @@ public class CarLaunchParamsModifierTest {
@Test
public void testIgnorePrivateDisplay() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
final int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(passengerUserId,
+ mUpdatable.setDisplayAllowListForUser(passengerUserId,
new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay10ForPassenger.getDisplayId()});
@@ -434,13 +490,13 @@ public class CarLaunchParamsModifierTest {
@Test
public void testDriverPassengerSwap() {
- mModifier.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
final int wasDriver = 10;
final int wasPassenger = 11;
mModifier.handleCurrentUserSwitching(wasDriver);
- mModifier.setDisplayAllowListForUser(wasPassenger,
+ mUpdatable.setDisplayAllowListForUser(wasPassenger,
new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
@@ -454,7 +510,7 @@ public class CarLaunchParamsModifierTest {
final int driver = wasPassenger;
final int passenger = wasDriver;
mModifier.handleCurrentUserSwitching(driver);
- mModifier.setDisplayAllowListForUser(passenger,
+ mUpdatable.setDisplayAllowListForUser(passenger,
new int[]{mDisplay10ForPassenger.getDisplayId(),
mDisplay11ForPassenger.getDisplayId()});
@@ -473,22 +529,23 @@ public class CarLaunchParamsModifierTest {
// When no sourcePreferredComponents is set, it doesn't set the display for system user.
assertNoDisplayIsAssigned(UserHandle.USER_SYSTEM);
- mModifier.setSourcePreferredComponents(true, null);
+ mUpdatable.setSourcePreferredComponents(true, null);
assertDisplayIsAssigned(UserHandle.USER_SYSTEM, mDisplayArea0ForDriver);
}
@Test
public void testPreferSourceForPassenger() {
- mModifier.setPassengerDisplays(new int[]{PASSENGER_DISPLAY_ID_10, PASSENGER_DISPLAY_ID_11});
+ mUpdatable.setPassengerDisplays(
+ new int[]{PASSENGER_DISPLAY_ID_10, PASSENGER_DISPLAY_ID_11});
int passengerUserId = 100;
- mModifier.setDisplayAllowListForUser(passengerUserId,
+ mUpdatable.setDisplayAllowListForUser(passengerUserId,
new int[]{PASSENGER_DISPLAY_ID_10, PASSENGER_DISPLAY_ID_11});
when(mActivityRecordSource.getDisplayArea()).thenReturn(mDisplayArea11ForPassenger);
// When no sourcePreferredComponents is set, it returns the default passenger display.
assertDisplayIsAssigned(passengerUserId, mDisplayArea10ForPassenger);
- mModifier.setSourcePreferredComponents(true, null);
+ mUpdatable.setSourcePreferredComponents(true, null);
assertDisplayIsAssigned(passengerUserId, mDisplayArea11ForPassenger);
}
@@ -497,7 +554,7 @@ public class CarLaunchParamsModifierTest {
when(mActivityOptions.getLaunchDisplayId()).thenReturn(PASSENGER_DISPLAY_ID_10);
when(mActivityRecordSource.getDisplayArea()).thenReturn(mDisplayArea0ForDriver);
- mModifier.setSourcePreferredComponents(true, null);
+ mUpdatable.setSourcePreferredComponents(true, null);
assertNoDisplayIsAssigned(UserHandle.USER_SYSTEM);
}
@@ -505,7 +562,7 @@ public class CarLaunchParamsModifierTest {
public void testPreferSourceForSpecifiedActivity() {
when(mActivityRecordSource.getDisplayArea()).thenReturn(mDisplayArea0ForDriver);
mActivityRecordActivity = buildActivityRecord("testPackage", "testActivity");
- mModifier.setSourcePreferredComponents(true,
+ mUpdatable.setSourcePreferredComponents(true,
Arrays.asList(new ComponentName("testPackage", "testActivity")));
assertDisplayIsAssigned(UserHandle.USER_SYSTEM, mDisplayArea0ForDriver);
@@ -515,7 +572,7 @@ public class CarLaunchParamsModifierTest {
public void testPreferSourceDoNotAssignDisplayForNonSpecifiedActivity() {
when(mActivityRecordSource.getDisplayArea()).thenReturn(mDisplayArea0ForDriver);
mActivityRecordActivity = buildActivityRecord("placeholderPackage", "placeholderActivity");
- mModifier.setSourcePreferredComponents(true,
+ mUpdatable.setSourcePreferredComponents(true,
Arrays.asList(new ComponentName("testPackage", "testActivity")));
assertNoDisplayIsAssigned(UserHandle.USER_SYSTEM);
@@ -565,9 +622,9 @@ public class CarLaunchParamsModifierTest {
.thenReturn(processName);
when(mActivityRecordActivity.getUid())
.thenReturn(processUid);
- mModifier.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
mDisplay10ForPassenger.getDisplayId()});
- mModifier.setDisplayAllowListForUser(userId,
+ mUpdatable.setDisplayAllowListForUser(userId,
new int[]{mDisplay10ForPassenger.getDisplayId()});
WindowProcessController controller = mock(WindowProcessController.class);
when(mActivityTaskManagerService.getProcessController(processName, processUid))
@@ -593,9 +650,9 @@ public class CarLaunchParamsModifierTest {
.thenReturn(launchedFromPid);
when(mActivityRecordActivity.getLaunchedFromUid())
.thenReturn(launchedFromUid);
- mModifier.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
mDisplay10ForPassenger.getDisplayId()});
- mModifier.setDisplayAllowListForUser(userId,
+ mUpdatable.setDisplayAllowListForUser(userId,
new int[]{mDisplay10ForPassenger.getDisplayId()});
WindowProcessController controller = mock(WindowProcessController.class);
when(mActivityTaskManagerService.getProcessController(launchedFromPid, launchedFromUid))
@@ -616,9 +673,9 @@ public class CarLaunchParamsModifierTest {
public void testSourceDisplayFromCallingDisplayIfAvailable() {
int userId = 10;
ActivityStarter.Request request = fakeRequest();
- mModifier.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
mDisplay10ForPassenger.getDisplayId()});
- mModifier.setDisplayAllowListForUser(userId,
+ mUpdatable.setDisplayAllowListForUser(userId,
new int[]{mDisplay10ForPassenger.getDisplayId()});
WindowProcessController controller = mock(WindowProcessController.class);
when(mActivityTaskManagerService.getProcessController(request.realCallingPid,
@@ -639,7 +696,7 @@ public class CarLaunchParamsModifierTest {
@Test
public void testSourceDisplayIgnoredIfNotInAllowList() {
ActivityStarter.Request request = fakeRequest();
- mModifier.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
+ mUpdatable.setPassengerDisplays(new int[]{mDisplay11ForPassenger.getDisplayId(),
mDisplay10ForPassenger.getDisplayId()});
WindowProcessController controller = mock(WindowProcessController.class);
when(mActivityTaskManagerService.getProcessController(anyString(), anyInt()))
@@ -658,7 +715,64 @@ public class CarLaunchParamsModifierTest {
.isEqualTo(mDisplayArea11ForPassenger);
}
- private ActivityStarter.Request fakeRequest() {
+ @Test
+ public void testSetPersistentActivityThrowsExceptionForInvalidDisplayId() {
+ ComponentName mapActivity = new ComponentName("testMapPkg", "mapActivity");
+ int invalidDisplayId = 999990;
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mUpdatable.setPersistentActivity(mapActivity,
+ invalidDisplayId, DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER));
+ }
+
+ @Test
+ public void testSetPersistentActivityThrowsExceptionForInvalidFeatureId() {
+ ComponentName mapActivity = new ComponentName("testMapPkg", "mapActivity");
+ int invalidFeatureId = 999990;
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mUpdatable.setPersistentActivity(mapActivity,
+ DEFAULT_DISPLAY, invalidFeatureId));
+ }
+
+ @Test
+ public void testPersistentActivityOverridesTDA() {
+ ComponentName mapActivityName = new ComponentName("testMapPkg", "mapActivity");
+ mActivityRecordActivity = buildActivityRecord(mapActivityName);
+
+ int ret = mUpdatable.setPersistentActivity(
+ mapActivityName, DEFAULT_DISPLAY, FEATURE_MAP_ID);
+ assertThat(ret).isEqualTo(CarActivityManager.RESULT_SUCCESS);
+
+ assertDisplayIsAssigned(UserHandle.USER_SYSTEM, mMapTaskDisplayArea);
+ }
+
+ @Test
+ public void testRemovePersistentActivity() {
+ ComponentName mapActivityName = new ComponentName("testMapPkg", "mapActivity");
+ mActivityRecordActivity = buildActivityRecord(mapActivityName);
+
+ int ret = mUpdatable.setPersistentActivity(
+ mapActivityName, DEFAULT_DISPLAY, FEATURE_MAP_ID);
+ assertThat(ret).isEqualTo(CarActivityManager.RESULT_SUCCESS);
+ // Removes the existing persistent Activity assignment.
+ ret = mUpdatable.setPersistentActivity(mapActivityName, DEFAULT_DISPLAY,
+ DisplayAreaOrganizer.FEATURE_UNDEFINED);
+ assertThat(ret).isEqualTo(CarActivityManager.RESULT_SUCCESS);
+
+ assertNoDisplayIsAssigned(UserHandle.USER_SYSTEM);
+ }
+
+ @Test
+ public void testRemoveUnknownPersistentActivityThrowsException() {
+ ComponentName mapActivity = new ComponentName("testMapPkg", "mapActivity");
+
+ assertThrows(ServiceSpecificException.class,
+ () -> mUpdatable.setPersistentActivity(mapActivity, DEFAULT_DISPLAY,
+ DisplayAreaOrganizer.FEATURE_UNDEFINED));
+ }
+
+ private static ActivityStarter.Request fakeRequest() {
ActivityStarter.Request request = new ActivityStarter.Request();
request.realCallingPid = 1324;
request.realCallingUid = 235;