Merge changes from topic "aae-carwatchdog-server"
* changes:
Add sepolicy for car watchdog
Initial implementation of car watchdog server
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index 5580745..1bbde70 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -1219,7 +1219,7 @@
package android.car.user {
public final class CarUserManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void addListener(@NonNull android.car.user.CarUserManager.UserLifecycleListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void addListener(@NonNull java.util.concurrent.Executor, @NonNull android.car.user.CarUserManager.UserLifecycleListener);
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void removeListener(@NonNull android.car.user.CarUserManager.UserLifecycleListener);
field public static final int USER_LIFECYCLE_EVENT_TYPE_STARTING = 1; // 0x1
field public static final int USER_LIFECYCLE_EVENT_TYPE_STOPPED = 5; // 0x5
diff --git a/car-lib/api/test-current.txt b/car-lib/api/test-current.txt
index b0d948e..27a9e3d 100644
--- a/car-lib/api/test-current.txt
+++ b/car-lib/api/test-current.txt
@@ -52,7 +52,7 @@
package android.car.user {
public final class CarUserManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void addListener(@NonNull android.car.user.CarUserManager.UserLifecycleListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void addListener(@NonNull java.util.concurrent.Executor, @NonNull android.car.user.CarUserManager.UserLifecycleListener);
method public int createUser(@Nullable String);
method public static String lifecycleEventTypeToString(int);
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void removeListener(@NonNull android.car.user.CarUserManager.UserLifecycleListener);
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 11aa3b9..bfec3da 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -63,6 +63,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.TransactionTooLargeException;
@@ -1538,12 +1539,19 @@
return;
} else if (mContext instanceof Service) {
Service service = (Service) mContext;
- throw new IllegalStateException("Car service has crashed, client not handle it:"
- + service.getPackageName() + "," + service.getClass().getSimpleName(),
- mConstructionStack);
+ killClient(service.getPackageName() + "," + service.getClass().getSimpleName());
+ } else {
+ killClient(/* clientInfo= */ null);
}
- throw new IllegalStateException("Car service crashed, client not handling it.",
+ }
+
+ private void killClient(@Nullable String clientInfo) {
+ Log.w(TAG_CAR, "**Car service has crashed. Client(" + clientInfo + ") is not handling it."
+ + " Client should use Car.createCar(..., CarServiceLifecycleListener, .."
+ + ".) to handle it properly. Check pritned callstack to check where other "
+ + "version of Car.createCar() was called. Killing the client process**",
mConstructionStack);
+ Process.killProcess(Process.myPid());
}
/** @hide */
diff --git a/car-lib/src/android/car/user/CarUserManager.java b/car-lib/src/android/car/user/CarUserManager.java
index c00a425..64c4388 100644
--- a/car-lib/src/android/car/user/CarUserManager.java
+++ b/car-lib/src/android/car/user/CarUserManager.java
@@ -21,6 +21,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.myUid;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,7 +39,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -50,6 +51,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* API to manage users related to car.
@@ -138,7 +140,7 @@
@Nullable
@GuardedBy("mLock")
- private ArraySet<UserLifecycleListener> mListeners;
+ private ArrayMap<UserLifecycleListener, Executor> mListeners;
@Nullable
@GuardedBy("mLock")
@@ -291,10 +293,12 @@
@SystemApi
@TestApi
@RequiresPermission(anyOf = {INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL})
- public void addListener(@NonNull UserLifecycleListener listener) {
+ public void addListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull UserLifecycleListener listener) {
checkInteractAcrossUsersPermission();
// TODO(b/144120654): add unit tests to validate input
+ // - executor cannot be null
// - listener cannot be null
// - listener must not be added before
@@ -312,10 +316,10 @@
}
if (mListeners == null) {
- mListeners = new ArraySet<>(1); // Most likely app will have just one listener
+ mListeners = new ArrayMap<>(1); // Most likely app will have just one listener
}
if (DBG) Log.d(TAG, "Adding listener: " + listener);
- mListeners.add(listener);
+ mListeners.put(listener, executor);
}
}
@@ -395,7 +399,7 @@
UserHandle fromHandle = resultData.getParcelable(BUNDLE_PARAM_PREVIOUS_USER_HANDLE);
int eventType = resultData.getInt(BUNDLE_PARAM_ACTION);
UserLifecycleEvent event = new UserLifecycleEvent(eventType, fromHandle, toHandle);
- ArraySet<UserLifecycleListener> listeners;
+ ArrayMap<UserLifecycleListener, Executor> listeners;
synchronized (mLock) {
listeners = mListeners;
}
@@ -404,9 +408,10 @@
return;
}
for (int i = 0; i < listeners.size(); i++) {
- UserLifecycleListener listener = listeners.valueAt(i);
+ UserLifecycleListener listener = listeners.keyAt(i);
+ Executor executor = listeners.valueAt(i);
if (DBG) Log.d(TAG, "Calling listener " + listener + " for event " + event);
- listener.onEvent(event);
+ executor.execute(() -> listener.onEvent(event));
}
}
}
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index 4e504f9..26f81b4 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -48,7 +48,6 @@
<!-- Allow smart unlock immediately after boot because the user shouldn't have to enter a pin
code to unlock their car head unit. -->
<bool name="config_strongAuthRequiredOnBoot">false</bool>
- <string name="config_defaultTrustAgent" translatable="false">com.android.car/com.android.car.trust.CarBleTrustAgent</string>
<!-- Show Navigation Bar -->
<bool name="config_showNavigationBar">true</bool>
diff --git a/computepipe/tests/PipeQueryTest.cpp b/computepipe/tests/PipeQueryTest.cpp
index 3a03391..634ebd0 100644
--- a/computepipe/tests/PipeQueryTest.cpp
+++ b/computepipe/tests/PipeQueryTest.cpp
@@ -106,7 +106,7 @@
addFakeRunner("dummy2", dummy2);
std::vector<std::string>* outNames = new std::vector<std::string>();
- std::unique_ptr<PipeQuery> qIface = std::make_unique<PipeQuery>(mRegistry);
+ std::shared_ptr<PipeQuery> qIface = ndk::SharedRefBase::make<PipeQuery>(mRegistry);
ASSERT_TRUE(qIface->getGraphList(outNames).isOk());
ASSERT_NE(outNames->size(), 0);
@@ -121,7 +121,7 @@
std::shared_ptr<IPipeRunner> dummy1 = ndk::SharedRefBase::make<FakeRunner>();
addFakeRunner("dummy1", dummy1);
- std::unique_ptr<PipeQuery> qIface = std::make_unique<PipeQuery>(mRegistry);
+ std::shared_ptr<PipeQuery> qIface = ndk::SharedRefBase::make<PipeQuery>(mRegistry);
std::shared_ptr<IClientInfo> info = ndk::SharedRefBase::make<FakeClientInfo>();
std::shared_ptr<IPipeRunner> runner;
ASSERT_TRUE(qIface->getPipeRunner("dummy1", info, &runner).isOk());
diff --git a/evs/apps/default/config.json b/evs/apps/default/config.json
index 0d968f5..b7fbd94 100644
--- a/evs/apps/default/config.json
+++ b/evs/apps/default/config.json
@@ -15,7 +15,7 @@
},
"cameras" : [
{
- "cameraId" : "/dev/video1",
+ "cameraId" : "/dev/video3",
"function" : "reverse, park",
"x" : 0.0,
"y" : -40.0,
diff --git a/evs/sampleDriver/resources/evs_sample_configuration.xml b/evs/sampleDriver/resources/evs_sample_configuration.xml
index 35f264e..fa3ada3 100644
--- a/evs/sampleDriver/resources/evs_sample_configuration.xml
+++ b/evs/sampleDriver/resources/evs_sample_configuration.xml
@@ -55,13 +55,13 @@
name='LOGICAL_MULTI_CAMERA_PHYSICAL_IDS'
type='byte[]'
size='2'
- value='/dev/video1,/dev/video2'
+ value='/dev/video3,/dev/video4'
/>
</characteristics>
</group>
<!-- camera device starts -->
- <device id='/dev/video1' position='rear'>
+ <device id='/dev/video3' position='rear'>
<caps>
<!-- list of supported controls -->
<supported_controls>
@@ -129,7 +129,7 @@
/>
</characteristics>
</device>
- <device id='/dev/video2' position='front'>
+ <device id='/dev/video4' position='front'>
<caps>
<!-- list of supported controls -->
<supported_controls>
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index 87bcd4d..179623d 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -1,10 +1,10 @@
# evs_mock mock hardware driver service
-type hal_evs_driver, domain, coredomain;
+type hal_evs_driver, domain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)
# allow init to launch processes in this context
-type hal_evs_driver_exec, exec_type, file_type, system_file_type;
+type hal_evs_driver_exec, exec_type, file_type, vendor_file_type;
init_daemon_domain(hal_evs_driver)
binder_use(hal_evs_driver)
diff --git a/evs/sepolicy/file_contexts b/evs/sepolicy/file_contexts
index 0ce34d1..7b8ec19 100644
--- a/evs/sepolicy/file_contexts
+++ b/evs/sepolicy/file_contexts
@@ -3,10 +3,10 @@
# Binaries associated with the default EVS stack, plus
# the directory which contains the configuration for the evs_app
#
-/system/bin/android\.hardware\.automotive\.evs@1\.[0-9]+-sample u:object_r:hal_evs_driver_exec:s0
/system/bin/android\.automotive\.evs\.manager@1\.[0-9]+ u:object_r:evs_manager_exec:s0
/system/bin/evs_app u:object_r:evs_app_exec:s0
/system/bin/evs_app_support_lib u:object_r:evs_app_exec:s0
/system/etc/automotive/evs(/.*)? u:object_r:evs_app_files:s0
+/vendor/bin/android\.hardware\.automotive\.evs@1\.[0-9]+-sample u:object_r:hal_evs_driver_exec:s0
###################################
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index 7662bcc..77d543e 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -33,6 +33,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -107,6 +108,8 @@
private boolean mIsBooting = true;
@GuardedBy("mLock")
private boolean mIsResuming;
+ @GuardedBy("mLock")
+ private boolean mRebootAfterGarageMode;
private final boolean mDisableUserSwitchDuringResume;
private final CarUserManagerHelper mCarUserManagerHelper;
private final UserManager mUserManager; // CarUserManagerHelper is deprecated...
@@ -248,7 +251,8 @@
writer.print(",mShutdownOnNextSuspend:" + mShutdownOnNextSuspend);
writer.print(",mShutdownOnFinish:" + mShutdownOnFinish);
writer.print(",sShutdownPrepareTimeMs:" + sShutdownPrepareTimeMs);
- writer.println(",mDisableUserSwitchDuringResume:" + mDisableUserSwitchDuringResume);
+ writer.print(",mDisableUserSwitchDuringResume:" + mDisableUserSwitchDuringResume);
+ writer.println(",mRebootAfterGarageMode:" + mRebootAfterGarageMode);
}
@Override
@@ -539,8 +543,21 @@
simulatedMode = mInSimulatedDeepSleepMode;
}
boolean mustShutDown;
+ boolean forceReboot;
synchronized (mLock) {
mustShutDown = mShutdownOnFinish && !simulatedMode;
+ forceReboot = mRebootAfterGarageMode;
+ mRebootAfterGarageMode = false;
+ }
+ if (forceReboot) {
+ PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+ if (powerManager == null) {
+ Log.wtf(CarLog.TAG_POWER, "No PowerManager. Cannot reboot.");
+ } else {
+ Log.i(CarLog.TAG_POWER, "GarageMode has completed. Forcing reboot.");
+ powerManager.reboot("GarageModeReboot");
+ throw new AssertionError("Should not return from PowerManager.reboot()");
+ }
}
if (mustShutDown) {
// shutdown HU
@@ -1119,18 +1136,23 @@
}
/**
- * Manually enter simulated suspend (Deep Sleep) mode
- * Invoked using "adb shell dumpsys activity service com.android.car suspend".
+ * Manually enter simulated suspend (Deep Sleep) mode, trigging Garage mode.
+ * If the parameter is 'true', reboot the system when Garage Mode completes.
+ *
+ * Invoked using "adb shell dumpsys activity service com.android.car suspend" or
+ * "adb shell dumpsys activity service com.android.car garage-mode reboot".
* This is similar to 'onApPowerStateChange()' except that it needs to create a CpmsState
* that is not directly derived from a VehicleApPowerStateReq.
*/
- public void forceSimulatedSuspend() {
+ @VisibleForTesting
+ void forceSuspendAndMaybeReboot(boolean shouldReboot) {
synchronized (mSimulationWaitObject) {
mInSimulatedDeepSleepMode = true;
mWakeFromSimulatedSleep = false;
}
PowerHandler handler;
synchronized (mLock) {
+ mRebootAfterGarageMode = shouldReboot;
mPendingPowerStates.addFirst(new CpmsState(CpmsState.SIMULATE_SLEEP,
CarPowerStateListener.SHUTDOWN_PREPARE));
handler = mHandler;
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 62bc3b1..acd73ab 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -781,6 +781,7 @@
private static final String PARAM_ON_MODE = "on";
private static final String PARAM_OFF_MODE = "off";
private static final String PARAM_QUERY_MODE = "query";
+ private static final String PARAM_REBOOT = "reboot";
private static final int RESULT_OK = 0;
private static final int RESULT_ERROR = -1; // Arbitrary value, any non-0 is fine
@@ -823,8 +824,9 @@
pw.println("\t Inject an error event from VHAL for testing.");
pw.println("\tenable-uxr true|false");
pw.println("\t Enable/Disable UX restrictions and App blocking.");
- pw.println("\tgarage-mode [on|off|query]");
- pw.println("\t Force into garage mode or check status.");
+ pw.println("\tgarage-mode [on|off|query|reboot]");
+ pw.println("\t Force into or out of garage mode, or check status.");
+ pw.println("\t With 'reboot', enter garage mode, then reboot when it completes.");
pw.println("\tget-do-activities pkgname");
pw.println("\t Get Distraction Optimized activities in given package.");
pw.println("\tget-carpropertyconfig [propertyId]");
@@ -846,7 +848,7 @@
pw.println("\t--metrics");
pw.println("\t When used with dumpsys, only metrics will be in the dumpsys output.");
pw.println("\tset-zoneid-for-uid [zoneid] [uid]");
- pw.println("\t Maps the audio zoneid to uid.");
+ pw.println("\t Maps the audio zoneid to uid.");
pw.println("\tstart-fixed-activity displayId packageName activityName");
pw.println("\t Start an Activity the specified display as fixed mode");
pw.println("\tstop-fixed-mode displayId");
@@ -981,7 +983,7 @@
writer.println("Resume: Simulating resuming from Deep Sleep");
break;
case COMMAND_SUSPEND:
- mCarPowerManagementService.forceSimulatedSuspend();
+ mCarPowerManagementService.forceSuspendAndMaybeReboot(false);
writer.println("Resume: Simulating powering down to Deep Sleep");
break;
case COMMAND_ENABLE_TRUSTED_DEVICE:
@@ -1300,9 +1302,13 @@
case PARAM_QUERY_MODE:
mGarageModeService.dump(writer);
break;
+ case PARAM_REBOOT:
+ mCarPowerManagementService.forceSuspendAndMaybeReboot(true);
+ writer.println("Entering Garage Mode. Will reboot when it completes.");
+ break;
default:
writer.println("Unknown value. Valid argument: " + PARAM_ON_MODE + "|"
- + PARAM_OFF_MODE + "|" + PARAM_QUERY_MODE);
+ + PARAM_OFF_MODE + "|" + PARAM_QUERY_MODE + "|" + PARAM_REBOOT);
}
}
diff --git a/service/src/com/android/car/hal/PowerHalService.java b/service/src/com/android/car/hal/PowerHalService.java
index 09d1bd0..cc71d25 100644
--- a/service/src/com/android/car/hal/PowerHalService.java
+++ b/service/src/com/android/car/hal/PowerHalService.java
@@ -275,6 +275,10 @@
} else if (brightness > 100) {
brightness = 100;
}
+ VehiclePropConfig prop = mProperties.get(DISPLAY_BRIGHTNESS);
+ if (prop == null) {
+ return;
+ }
try {
mHal.set(VehicleProperty.DISPLAY_BRIGHTNESS, 0).to(brightness);
Log.i(CarLog.TAG_POWER, "send display brightness = " + brightness);
diff --git a/tests/CarDeveloperOptions/res/xml/accessibility_settings.xml b/tests/CarDeveloperOptions/res/xml/accessibility_settings.xml
index b170ca0..3c98a00 100644
--- a/tests/CarDeveloperOptions/res/xml/accessibility_settings.xml
+++ b/tests/CarDeveloperOptions/res/xml/accessibility_settings.xml
@@ -21,11 +21,6 @@
android:title="@string/accessibility_settings"
android:persistent="true">
- <Preference
- android:key="accessibility_shortcut_preference"
- android:fragment="com.android.car.developeroptions.accessibility.AccessibilityShortcutPreferenceFragment"
- android:title="@string/accessibility_global_gesture_preference_title"/>
-
<PreferenceCategory
android:key="user_installed_services_category"
android:title="@string/user_installed_services_category_title">
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/accessibility/AccessibilitySettings.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/accessibility/AccessibilitySettings.java
index df096bd..0c8b62c 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/accessibility/AccessibilitySettings.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/accessibility/AccessibilitySettings.java
@@ -52,16 +52,16 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
-import com.android.internal.accessibility.AccessibilityShortcutController;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.view.RotationPolicy;
-import com.android.internal.view.RotationPolicy.RotationPolicyListener;
import com.android.car.developeroptions.R;
import com.android.car.developeroptions.SettingsPreferenceFragment;
import com.android.car.developeroptions.Utils;
import com.android.car.developeroptions.display.DarkUIPreferenceController;
import com.android.car.developeroptions.display.ToggleFontSizePreferenceFragment;
import com.android.car.developeroptions.search.BaseSearchIndexProvider;
+import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.view.RotationPolicy;
+import com.android.internal.view.RotationPolicy.RotationPolicyListener;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
@@ -236,7 +236,6 @@
private Preference mDisplayMagnificationPreferenceScreen;
private Preference mFontSizePreferenceScreen;
private Preference mAutoclickPreferenceScreen;
- private Preference mAccessibilityShortcutPreferenceScreen;
private Preference mDisplayDaltonizerPreferenceScreen;
private Preference mHearingAidPreference;
private Preference mVibrationPreferenceScreen;
@@ -514,9 +513,6 @@
// Display color adjustments.
mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
- // Accessibility shortcut.
- mAccessibilityShortcutPreferenceScreen = findPreference(ACCESSIBILITY_SHORTCUT_PREFERENCE);
-
// Vibrations.
mVibrationPreferenceScreen = findPreference(VIBRATION_PREFERENCE_SCREEN);
@@ -759,8 +755,6 @@
updateAutoclickSummary(mAutoclickPreferenceScreen);
- updateAccessibilityShortcut(mAccessibilityShortcutPreferenceScreen);
-
updateAccessibilityTimeoutSummary(getContentResolver(),
findPreference(ACCESSIBILITY_CONTENT_TIMEOUT_PREFERENCE));
updateAccessibilityTimeoutSummary(getContentResolver(),
@@ -936,23 +930,6 @@
mToggleMasterMonoPreference.setChecked(masterMono);
}
- private void updateAccessibilityShortcut(Preference preference) {
- if (AccessibilityManager.getInstance(getActivity())
- .getInstalledAccessibilityServiceList().isEmpty()) {
- mAccessibilityShortcutPreferenceScreen
- .setSummary(getString(R.string.accessibility_no_services_installed));
- mAccessibilityShortcutPreferenceScreen.setEnabled(false);
- } else {
- mAccessibilityShortcutPreferenceScreen.setEnabled(true);
- boolean shortcutEnabled =
- AccessibilityUtils.isShortcutEnabled(getContext(), UserHandle.myUserId());
- CharSequence summary = shortcutEnabled
- ? AccessibilityShortcutPreferenceFragment.getServiceName(getContext())
- : getString(R.string.accessibility_feature_state_off);
- mAccessibilityShortcutPreferenceScreen.setSummary(summary);
- }
- }
-
private static void configureMagnificationPreferenceIfNeeded(Preference preference) {
// Some devices support only a single magnification mode. In these cases, we redirect to
// the magnification mode's UI directly, rather than showing a PreferenceScreen with a
diff --git a/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
index 1c7c9e9..7fb9066 100644
--- a/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
+++ b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
@@ -21,10 +21,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doNothing;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.car.Car;
import android.car.storagemonitoring.CarStorageMonitoringManager;
import android.car.storagemonitoring.IoStats;
@@ -33,6 +33,7 @@
import android.car.storagemonitoring.UidIoRecord;
import android.car.storagemonitoring.WearEstimate;
import android.car.storagemonitoring.WearEstimateChange;
+import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.util.JsonWriter;
@@ -58,7 +59,6 @@
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import java.io.File;
import java.io.FileWriter;
@@ -308,8 +308,10 @@
private CarStorageMonitoringManager mCarStorageMonitoringManager;
- private ArgumentCaptor<Intent> mBroadcastIntentArg = ArgumentCaptor.forClass(Intent.class);
- private ArgumentCaptor<String> mBroadcastStringArg = ArgumentCaptor.forClass(String.class);
+ @Override
+ protected MockedCarTestContext createMockedCarTestContext(Context context) {
+ return new CarStorageMonitoringTestContext(context);
+ }
@Override
protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() {
@@ -382,10 +384,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- doNothing().when(getCarServiceContext()).sendBroadcast(mBroadcastIntentArg.capture(),
- mBroadcastStringArg.capture());
mMockSystemStateInterface.executeBootCompletedActions();
-
mCarStorageMonitoringManager =
(CarStorageMonitoringManager) getCar().getCarManager(Car.STORAGE_MONITORING_SERVICE);
}
@@ -656,7 +655,9 @@
mMockStorageMonitoringInterface.addIoStatsRecord(record);
mMockTimeInterface.setUptime(500).tick();
- assertBroadcastArgs(mBroadcastIntentArg.getValue(), mBroadcastStringArg.getValue());
+ CarStorageMonitoringTestContext context = (CarStorageMonitoringTestContext) getContext();
+ assertBroadcastArgs(context.getLastBroadcastedIntent(),
+ context.getLastBroadcastedString());
}
@Test
@@ -678,7 +679,9 @@
mMockStorageMonitoringInterface.addIoStatsRecord(record);
mMockTimeInterface.setUptime(500).tick();
- assertBroadcastArgs(mBroadcastIntentArg.getValue(), mBroadcastStringArg.getValue());
+ CarStorageMonitoringTestContext context = (CarStorageMonitoringTestContext) getContext();
+ assertBroadcastArgs(context.getLastBroadcastedIntent(),
+ context.getLastBroadcastedString());
}
@Test
@@ -773,6 +776,35 @@
}
+ /**
+ * Special version of {@link MockedCarTestContext} that stores the last arguments used when
+ * invoking {@method sendBroadcast(Intent, String)} to be retrieved later by the test.
+ */
+ private class CarStorageMonitoringTestContext extends MockedCarTestContext {
+ private Intent mLastBroadcastedIntent;
+ private String mLastBroadcastedString;
+
+ CarStorageMonitoringTestContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public void sendBroadcast(@RequiresPermission Intent intent,
+ @Nullable String receiverPermission) {
+ mLastBroadcastedIntent = intent;
+ mLastBroadcastedString = receiverPermission;
+ super.sendBroadcast(intent, receiverPermission);
+ }
+
+ Intent getLastBroadcastedIntent() {
+ return mLastBroadcastedIntent;
+ }
+
+ String getLastBroadcastedString() {
+ return mLastBroadcastedString;
+ }
+ }
+
static final class MockStorageMonitoringInterface implements StorageMonitoringInterface,
WearInformationProvider {
private WearInformation mWearInformation = null;
diff --git a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index 50db8b4..cdff2ac 100644
--- a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -15,13 +15,10 @@
*/
package com.android.car;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
@@ -30,6 +27,7 @@
import android.car.test.CarTestManagerBinderWrapper;
import android.content.ComponentName;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Resources;
@@ -108,6 +106,8 @@
private static final IBinder mCarServiceToken = new Binder();
private static boolean mRealCarServiceReleased = false;
+ private MockedCarTestContext mMockedCarTestContext;
+
protected synchronized MockedVehicleHal createMockedVehicleHal() {
return new MockedVehicleHal();
}
@@ -146,8 +146,16 @@
new String[0]);
}
- protected Context getContext() {
- return InstrumentationRegistry.getInstrumentation().getTargetContext();
+ protected synchronized Context getContext() {
+ if (mMockedCarTestContext == null) {
+ mMockedCarTestContext = createMockedCarTestContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ }
+ return mMockedCarTestContext;
+ }
+
+ protected MockedCarTestContext createMockedCarTestContext(Context context) {
+ return new MockedCarTestContext(context);
}
protected Context getTestContext() {
@@ -186,11 +194,8 @@
mFakeSystemInterface = getSystemInterfaceBuilder().build();
configureFakeSystemInterface();
- Context context = getCarServiceContext();
- spyOn(context);
- mResources = new MockResources(context.getResources());
- configureResourceOverrides(mResources);
- doReturn(mResources).when(context).getResources();
+ mMockedCarTestContext = (MockedCarTestContext) getContext();
+ configureResourceOverrides((MockResources) mMockedCarTestContext.getResources());
doAnswer((invocation) -> {
CarUserService.UserCallback callback = invocation.getArgument(0);
@@ -210,12 +215,12 @@
// This prevents one test failure in tearDown from triggering assertion failure for single
// CarLocalServices service.
CarLocalServices.removeAllServices();
- mCarImpl = new ICarImpl(context, mMockedVehicleHal, mFakeSystemInterface,
+ mCarImpl = new ICarImpl(mMockedCarTestContext, mMockedVehicleHal, mFakeSystemInterface,
/* errorNotifier= */ null , "MockedCar", mCarUserService);
spyOnInitMockedHal();
initMockedHal(mCarImpl, false /* no need to release */);
- mCar = new Car(context, mCarImpl, null /* handler */);
+ mCar = new Car(mMockedCarTestContext, mCarImpl, null /* handler */);
}
@After
@@ -243,10 +248,6 @@
return (VmsClientManager) mCarImpl.getCarInternalService(ICarImpl.INTERNAL_VMS_MANAGER);
}
- protected Context getCarServiceContext() {
- return getContext();
- }
-
protected synchronized void reinitializeMockedHal() throws Exception {
initMockedHal(mCarImpl, true /* release */);
}
@@ -404,6 +405,29 @@
}
}
+ /**
+ * Special version of {@link ContextWrapper} that overrides {@method getResources} by returning
+ * a {@link MockResources}, so tests are free to set resources. This class represents an
+ * alternative of using Mockito spy (see b/148240178).
+ *
+ * Tests may specialize this class. If they decide so, then they are required to override
+ * {@method newMockedCarContext} to provide their own context.
+ */
+ protected static class MockedCarTestContext extends ContextWrapper {
+
+ private final Resources mMockedResources;
+
+ MockedCarTestContext(Context base) {
+ super(base);
+ mMockedResources = new MockResources(base.getResources());
+ }
+
+ @Override
+ public Resources getResources() {
+ return mMockedResources;
+ }
+ }
+
static final class MockResources extends Resources {
private final HashMap<Integer, Boolean> mBooleanOverrides = new HashMap<>();
private final HashMap<Integer, Integer> mIntegerOverrides = new HashMap<>();