/* * 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.car; import static android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS; import static android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME; import static android.car.Car.PERMISSION_CAR_POWER; import static android.car.Car.PERMISSION_CONTROL_CAR_POWER_POLICY; import static android.car.Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG; import static android.car.Car.PERMISSION_USE_CAR_WATCHDOG; import static android.car.VehicleAreaSeat.SEAT_UNKNOWN; import static android.car.settings.CarSettings.Global.FORCED_DAY_NIGHT_MODE; import static android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_SUCCEEDED; import static android.hardware.automotive.vehicle.UserIdentificationAssociationSetValue.ASSOCIATE_CURRENT_USER; import static android.hardware.automotive.vehicle.UserIdentificationAssociationSetValue.DISASSOCIATE_ALL_USERS; import static android.hardware.automotive.vehicle.UserIdentificationAssociationSetValue.DISASSOCIATE_CURRENT_USER; import static android.hardware.automotive.vehicle.UserIdentificationAssociationType.CUSTOM_1; import static android.hardware.automotive.vehicle.UserIdentificationAssociationType.CUSTOM_2; import static android.hardware.automotive.vehicle.UserIdentificationAssociationType.CUSTOM_3; import static android.hardware.automotive.vehicle.UserIdentificationAssociationType.CUSTOM_4; import static android.hardware.automotive.vehicle.UserIdentificationAssociationType.KEY_FOB; import static android.media.AudioManager.FLAG_SHOW_UI; import static com.android.car.CarServiceUtils.toIntArray; import static com.android.car.hal.property.HalPropertyDebugUtils.toAreaIdString; import static com.android.car.hal.property.HalPropertyDebugUtils.toPropertyIdString; import static com.android.car.hal.property.HalPropertyDebugUtils.toPropertyId; import static com.android.car.power.PolicyReader.POWER_STATE_ON; import static com.android.car.power.PolicyReader.POWER_STATE_WAIT_FOR_VHAL; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.UiModeManager; import android.car.Car; import android.car.CarOccupantZoneManager; import android.car.CarVersion; import android.car.SyncResultCallback; import android.car.VehiclePropertyIds; import android.car.builtin.content.pm.PackageManagerHelper; import android.car.builtin.display.DisplayManagerHelper; import android.car.builtin.input.InputManagerHelper; import android.car.builtin.os.BuildHelper; import android.car.builtin.os.UserManagerHelper; import android.car.builtin.util.Slogf; import android.car.builtin.widget.LockPatternHelper; import android.car.content.pm.CarPackageManager; import android.car.drivingstate.CarUxRestrictions; import android.car.feature.Flags; import android.car.input.CarInputManager; import android.car.input.CustomInputEvent; import android.car.input.RotaryEvent; import android.car.media.IAudioZonesMirrorStatusCallback; import android.car.telemetry.CarTelemetryManager; import android.car.telemetry.TelemetryProto.TelemetryError; import android.car.user.CarUserManager; import android.car.user.UserCreationRequest; import android.car.user.UserCreationResult; import android.car.user.UserIdentificationAssociationResponse; import android.car.user.UserRemovalRequest; import android.car.user.UserRemovalResult; import android.car.user.UserSwitchRequest; import android.car.user.UserSwitchResult; import android.car.util.concurrent.AsyncFuture; import android.car.watchdog.CarWatchdogManager; import android.car.watchdog.IoOveruseConfiguration; import android.car.watchdog.PerStateBytes; import android.car.watchdog.ResourceOveruseConfiguration; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.hardware.automotive.vehicle.CreateUserRequest; import android.hardware.automotive.vehicle.CreateUserStatus; import android.hardware.automotive.vehicle.InitialUserInfoResponse; import android.hardware.automotive.vehicle.InitialUserInfoResponseAction; import android.hardware.automotive.vehicle.RemoveUserRequest; import android.hardware.automotive.vehicle.SwitchUserMessageType; import android.hardware.automotive.vehicle.SwitchUserRequest; import android.hardware.automotive.vehicle.SwitchUserStatus; import android.hardware.automotive.vehicle.UserIdentificationAssociation; import android.hardware.automotive.vehicle.UserIdentificationAssociationValue; import android.hardware.automotive.vehicle.UserIdentificationGetRequest; import android.hardware.automotive.vehicle.UserIdentificationResponse; import android.hardware.automotive.vehicle.UserIdentificationSetAssociation; import android.hardware.automotive.vehicle.UserIdentificationSetRequest; import android.hardware.automotive.vehicle.UserInfo; import android.hardware.automotive.vehicle.UsersInfo; import android.hardware.automotive.vehicle.VehicleArea; import android.hardware.automotive.vehicle.VehicleDisplay; import android.hardware.automotive.vehicle.VehicleGear; import android.hardware.automotive.vehicle.VehiclePropError; import android.hardware.display.DisplayManager; import android.hardware.input.InputManager; import android.os.Binder; import android.os.FileUtils; import android.os.IBinder; import android.os.NewUserRequest; import android.os.NewUserResponse; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; import android.util.SparseArray; import android.view.Display; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import com.android.car.am.FixedActivityService; import com.android.car.audio.CarAudioService; import com.android.car.evs.CarEvsService; import com.android.car.garagemode.GarageModeService; import com.android.car.hal.HalCallback; import com.android.car.hal.HalPropConfig; import com.android.car.hal.HalPropValue; import com.android.car.hal.InputHalService; import com.android.car.hal.PowerHalService; import com.android.car.hal.UserHalHelper; import com.android.car.hal.UserHalService; import com.android.car.hal.VehicleHal; import com.android.car.internal.util.DebugUtils; import com.android.car.internal.util.IndentingPrintWriter; import com.android.car.pm.CarPackageManagerService; import com.android.car.power.CarPowerManagementService; import com.android.car.systeminterface.SystemInterface; import com.android.car.telemetry.CarTelemetryService; import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutor; import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutorListener; import com.android.car.telemetry.util.IoUtils; import com.android.car.user.CarUserService; import com.android.car.user.UserHandleHelper; import com.android.car.watchdog.CarWatchdogService; import com.android.internal.util.Preconditions; import com.android.modules.utils.BasicShellCommandHandler; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; final class CarShellCommand extends BasicShellCommandHandler { private static final String NO_INITIAL_USER = "N/A"; private static final String TAG = CarLog.tagFor(CarShellCommand.class); private static final boolean VERBOSE = false; private static final String COMMAND_HELP = "-h"; private static final String COMMAND_DAY_NIGHT_MODE = "day-night-mode"; private static final String COMMAND_INJECT_VHAL_EVENT = "inject-vhal-event"; private static final String COMMAND_INJECT_ERROR_EVENT = "inject-error-event"; private static final String COMMAND_INJECT_CONTINUOUS_EVENT = "inject-continuous-events"; private static final String COMMAND_ENABLE_UXR = "enable-uxr"; private static final String COMMAND_GARAGE_MODE = "garage-mode"; private static final String COMMAND_GET_DO_ACTIVITIES = "get-do-activities"; private static final String COMMAND_GET_CARPROPERTYCONFIG = "get-carpropertyconfig"; private static final String COMMAND_GET_PROPERTY_VALUE = "get-property-value"; private static final String COMMAND_SET_PROPERTY_VALUE = "set-property-value"; private static final String COMMAND_PROJECTION_AP_TETHERING = "projection-tethering"; private static final String COMMAND_PROJECTION_AP_STABLE_CONFIG = "projection-stable-lohs-config"; private static final String COMMAND_PROJECTION_UI_MODE = "projection-ui-mode"; private static final String COMMAND_RESUME = "resume"; private static final String COMMAND_SUSPEND = "suspend"; private static final String COMMAND_HIBERNATE = "hibernate"; private static final String COMMAND_SET_DISPLAY_STATE = "set-display-state"; private static final String PARAM_SIMULATE = "--simulate"; private static final String PARAM_REAL = "--real"; private static final String PARAM_AUTO = "--auto"; private static final String PARAM_SKIP_GARAGEMODE = "--skip-garagemode"; private static final String PARAM_REBOOT = "--reboot"; private static final String PARAM_WAKEUP_AFTER = "--wakeup-after"; private static final String PARAM_CANCEL_AFTER = "--cancel-after"; private static final String PARAM_FREE_MEMORY = "--free-memory"; private static final String COMMAND_SET_UID_TO_ZONE = "set-audio-zone-for-uid"; private static final String COMMAND_RESET_VOLUME_CONTEXT = "reset-selected-volume-context"; private static final String COMMAND_SET_MUTE_CAR_VOLUME_GROUP = "set-mute-car-volume-group"; private static final String COMMAND_SET_GROUP_VOLUME = "set-group-volume"; private static final String COMMAND_SET_AUDIO_MIRROR = "set-audio-mirror"; private static final String COMMAND_UNSET_AUDIO_MIRROR = "unset-audio-mirror"; private static final String COMMAND_START_FIXED_ACTIVITY_MODE = "start-fixed-activity-mode"; private static final String COMMAND_STOP_FIXED_ACTIVITY_MODE = "stop-fixed-activity-mode"; private static final String COMMAND_ENABLE_FEATURE = "enable-feature"; private static final String COMMAND_DISABLE_FEATURE = "disable-feature"; private static final String COMMAND_INJECT_KEY = "inject-key"; private static final String COMMAND_INJECT_MOTION = "inject-motion"; private static final String COMMAND_INJECT_ROTARY = "inject-rotary"; private static final String COMMAND_INJECT_CUSTOM_INPUT = "inject-custom-input"; private static final String COMMAND_CHECK_LOCK_IS_SECURE = "check-lock-is-secure"; private static final String COMMAND_CHECK_FAKE_VHAL = "check-fake-vhal"; private static final String COMMAND_GET_INITIAL_USER_INFO = "get-initial-user-info"; private static final String COMMAND_SWITCH_USER = "switch-user"; private static final String COMMAND_LOGOUT_USER = "logout-user"; private static final String COMMAND_REMOVE_USER = "remove-user"; private static final String COMMAND_CREATE_USER = "create-user"; private static final String COMMAND_GET_INITIAL_USER = "get-initial-user"; private static final String COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE = "set-occupant-zone-for-user"; private static final String COMMAND_RESET_USER_ID_IN_OCCUPANT_ZONE = "reset-user-in-occupant-zone"; private static final String COMMAND_GET_USER_AUTH_ASSOCIATION = "get-user-auth-association"; private static final String COMMAND_SET_USER_AUTH_ASSOCIATION = "set-user-auth-association"; private static final String COMMAND_SET_START_BG_USERS_ON_GARAGE_MODE = "set-start-bg-users-on-garage-mode"; private static final String COMMAND_DEFINE_POWER_POLICY = "define-power-policy"; private static final String COMMAND_APPLY_POWER_POLICY = "apply-power-policy"; private static final String COMMAND_DEFINE_POWER_POLICY_GROUP = "define-power-policy-group"; private static final String COMMAND_SET_POWER_POLICY_GROUP = "set-power-policy-group"; private static final String COMMAND_APPLY_CTS_VERIFIER_POWER_OFF_POLICY = "apply-cts-verifier-power-off-policy"; private static final String COMMAND_APPLY_CTS_VERIFIER_POWER_ON_POLICY = "apply-cts-verifier-power-on-policy"; private static final String COMMAND_POWER_OFF = "power-off"; private static final String COMMAND_SILENT_MODE = "silent-mode"; // Used with COMMAND_SILENT_MODE for forced silent: "forced-silent" private static final String SILENT_MODE_FORCED_SILENT = CarPowerManagementService.SILENT_MODE_FORCED_SILENT; // Used with COMMAND_SILENT_MODE for forced non silent: "forced-non-silent" private static final String SILENT_MODE_FORCED_NON_SILENT = CarPowerManagementService.SILENT_MODE_FORCED_NON_SILENT; // Used with COMMAND_SILENT_MODE for non forced silent mode: "non-forced-silent-mode" private static final String SILENT_MODE_NON_FORCED = CarPowerManagementService.SILENT_MODE_NON_FORCED; private static final String COMMAND_EMULATE_DRIVING_STATE = "emulate-driving-state"; private static final String DRIVING_STATE_DRIVE = "drive"; private static final String DRIVING_STATE_PARK = "park"; private static final String DRIVING_STATE_REVERSE = "reverse"; private static final String DRIVING_STATE_NEUTRAL = "neutral"; private static final String COMMAND_SET_REARVIEW_CAMERA_ID = "set-rearview-camera-id"; private static final String COMMAND_GET_REARVIEW_CAMERA_ID = "get-rearview-camera-id"; private static final String COMMAND_SET_CAMERA_ID = "set-camera-id"; private static final String COMMAND_GET_CAMERA_ID = "get-camera-id"; private static final String COMMAND_ENABLE_CAMERA_SERVICE_TYPE = "enable-camera-service-type"; private static final String COMMAND_CHECK_CAMERA_SERVICE_TYPE_ENABLED = "check-camera-service-type-enabled"; private static final String COMMAND_WATCHDOG_CONTROL_PACKAGE_KILLABLE_STATE = "watchdog-control-package-killable-state"; private static final String COMMAND_WATCHDOG_IO_SET_3P_FOREGROUND_BYTES = "watchdog-io-set-3p-foreground-bytes"; private static final String COMMAND_WATCHDOG_IO_GET_3P_FOREGROUND_BYTES = "watchdog-io-get-3p-foreground-bytes"; private static final String COMMAND_WATCHDOG_CONTROL_PROCESS_HEALTH_CHECK = "watchdog-control-health-check"; private static final String COMMAND_WATCHDOG_RESOURCE_OVERUSE_KILL = "watchdog-resource-overuse-kill"; private static final String COMMAND_DRIVING_SAFETY_SET_REGION = "set-drivingsafety-region"; private static final String COMMAND_TELEMETRY = "telemetry"; private static final String COMMAND_CONTROL_COMPONENT_ENABLED_STATE = "control-component-enabled-state"; private static final String COMMAND_LIST_VHAL_PROPS = "list-vhal-props"; private static final String COMMAND_GET_VHAL_BACKEND = "get-vhal-backend"; private static final String COMMAND_TEST_ECHO_REVERSE_BYTES = "test-echo-reverse-bytes"; private static final String COMMAND_GET_TARGET_CAR_VERSION = "get-target-car-version"; private static final String COMMAND_SET_PROCESS_GROUP = "set-process-group"; private static final String COMMAND_GET_PROCESS_GROUP = "get-process-group"; private static final String COMMAND_SET_PROCESS_PROFILE = "set-process-profile"; private static final String COMMAND_GET_DISPLAY_BY_USER = "get-display-by-user"; private static final String COMMAND_GET_USER_BY_DISPLAY = "get-user-by-display"; private static final String COMMAND_GENERATE_TEST_VENDOR_CONFIGS = "gen-test-vendor-configs"; private static final String COMMAND_RESTORE_TEST_VENDOR_CONFIGS = "restore-vendor-configs"; private static final String COMMAND_GET_CURRENT_UX_RESTRICTIONS = "get-current-ux-restrictions"; private static final String COMMAND_SET_CURRENT_UXR_MODE = "set-current-uxr-mode"; private static final String COMMAND_GET_CURRENT_UXR_MODE = "get-current-uxr-mode"; private static final String COMMAND_GET_SUPPORTED_UXR_MODES = "get-supported-uxr-modes"; private static final String COMMAND_GET_UXR_CONFIG = "get-uxr-config"; private static final String COMMAND_GET_INPUT_AND_DISPLAY_INFO = "get-input-and-display-info"; private static final String COMMAND_ADD_INPUT_DESCRIPTOR_ASSOCIATION_TO_DISPLAY_UNIQUE_ID = "add-input-descriptor-association-to-display-unique-id"; private static final String COMMAND_REMOVE_INPUT_DESCRIPTOR_ASSOCIATION = "remove-input-descriptor-association"; private static final String[] CREATE_OR_MANAGE_USERS_PERMISSIONS = new String[] { android.Manifest.permission.CREATE_USERS, android.Manifest.permission.MANAGE_USERS }; private static final String[] CREATE_OR_MANAGE_OR_QUERY_USERS_PERMISSIONS = new String[] { android.Manifest.permission.CREATE_USERS, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS }; // List of commands allowed in user build. All these command should be protected with // a permission. K: command, V: required permissions (must have at least 1). // Only commands with permission already granted to shell user should be allowed. // Commands that can affect safety should be never allowed in user build. // // This map is looked up first, then USER_BUILD_COMMAND_TO_PERMISSION_MAP private static final ArrayMap USER_BUILD_COMMAND_TO_PERMISSIONS_MAP; static { USER_BUILD_COMMAND_TO_PERMISSIONS_MAP = new ArrayMap<>(8); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_GET_INITIAL_USER_INFO, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_SWITCH_USER, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_LOGOUT_USER, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_REMOVE_USER, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_CREATE_USER, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_GET_USER_AUTH_ASSOCIATION, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_SET_USER_AUTH_ASSOCIATION, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_SET_START_BG_USERS_ON_GARAGE_MODE, CREATE_OR_MANAGE_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_GET_DISPLAY_BY_USER, CREATE_OR_MANAGE_OR_QUERY_USERS_PERMISSIONS); USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_GET_USER_BY_DISPLAY, CREATE_OR_MANAGE_OR_QUERY_USERS_PERMISSIONS); } // List of commands allowed in user build. All these command should be protected with // a permission. K: command, V: required permission. // Only commands with permission already granted to shell user should be allowed. // Commands that can affect safety should be never allowed in user build. private static final ArrayMap USER_BUILD_COMMAND_TO_PERMISSION_MAP; static { USER_BUILD_COMMAND_TO_PERMISSION_MAP = new ArrayMap<>(27); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GARAGE_MODE, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_RESUME, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SUSPEND, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_HIBERNATE, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SET_DISPLAY_STATE, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_POWER_OFF, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_DEFINE_POWER_POLICY, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_APPLY_POWER_POLICY, PERMISSION_CONTROL_CAR_POWER_POLICY); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_DEFINE_POWER_POLICY_GROUP, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SET_POWER_POLICY_GROUP, PERMISSION_CONTROL_CAR_POWER_POLICY); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_APPLY_CTS_VERIFIER_POWER_OFF_POLICY, PERMISSION_CONTROL_CAR_POWER_POLICY); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_APPLY_CTS_VERIFIER_POWER_ON_POLICY, PERMISSION_CONTROL_CAR_POWER_POLICY); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SILENT_MODE, PERMISSION_CAR_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_INITIAL_USER, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_DAY_NIGHT_MODE, android.Manifest.permission.MODIFY_DAY_NIGHT_MODE); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_RESET_VOLUME_CONTEXT, PERMISSION_CAR_CONTROL_AUDIO_VOLUME); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SET_MUTE_CAR_VOLUME_GROUP, PERMISSION_CAR_CONTROL_AUDIO_VOLUME); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SET_GROUP_VOLUME, PERMISSION_CAR_CONTROL_AUDIO_VOLUME); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SET_AUDIO_MIRROR, PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_UNSET_AUDIO_MIRROR, PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_INJECT_KEY, android.Manifest.permission.INJECT_EVENTS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_INJECT_MOTION, android.Manifest.permission.INJECT_EVENTS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_INJECT_ROTARY, android.Manifest.permission.INJECT_EVENTS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_WATCHDOG_CONTROL_PACKAGE_KILLABLE_STATE, PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_WATCHDOG_IO_SET_3P_FOREGROUND_BYTES, PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_WATCHDOG_IO_GET_3P_FOREGROUND_BYTES, PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_WATCHDOG_CONTROL_PROCESS_HEALTH_CHECK, PERMISSION_USE_CAR_WATCHDOG); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_WATCHDOG_RESOURCE_OVERUSE_KILL, PERMISSION_USE_CAR_WATCHDOG); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_CONTROL_COMPONENT_ENABLED_STATE, android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); // borrow the permission to pass assertHasAtLeastOnePermission() for a user build USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_CHECK_LOCK_IS_SECURE, android.Manifest.permission.INJECT_EVENTS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_TEST_ECHO_REVERSE_BYTES, android.car.Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_TARGET_CAR_VERSION, android.Manifest.permission.QUERY_ALL_PACKAGES); } private static final String PARAM_DAY_MODE = "day"; private static final String PARAM_NIGHT_MODE = "night"; private static final String PARAM_SENSOR_MODE = "sensor"; private static final String PARAM_VEHICLE_PROPERTY_GLOBAL_AREA_ID = "0"; private static final String PARAM_INJECT_EVENT_DEFAULT_RATE = "10"; private static final String PARAM_INJECT_EVENT_DEFAULT_DURATION = "60"; private static final String PARAM_ALL_PROPERTIES_OR_AREA_IDS = "-1"; 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_AFTER_GARAGEMODE = "reboot"; private static final String PARAM_MUTE = "mute"; private static final String PARAM_UNMUTE = "unmute"; private static final int RESULT_OK = 0; private static final int RESULT_ERROR = -1; // Arbitrary value, any non-0 is fine private static final int DEFAULT_HAL_TIMEOUT_MS = 1_000; private static final int DEFAULT_CAR_USER_SERVICE_TIMEOUT_MS = 60_000; private static final int INVALID_USER_AUTH_TYPE_OR_VALUE = -1; private static final SparseArray VALID_USER_AUTH_TYPES; private static final String VALID_USER_AUTH_TYPES_HELP; private static final SparseArray VALID_USER_AUTH_SET_VALUES; private static final String VALID_USER_AUTH_SET_VALUES_HELP; private static final ArrayMap CUSTOM_INPUT_FUNCTION_ARGS; private static final int DEFAULT_DEVICE_ID = 0; private static final float DEFAULT_PRESSURE = 1.0f; private static final float NO_PRESSURE = 0.0f; private static final float DEFAULT_SIZE = 1.0f; private static final int DEFAULT_META_STATE = 0; private static final float DEFAULT_PRECISION_X = 1.0f; private static final float DEFAULT_PRECISION_Y = 1.0f; private static final int DEFAULT_EDGE_FLAGS = 0; private static final int DEFAULT_BUTTON_STATE = 0; private static final int DEFAULT_FLAGS = 0; private static final ArrayMap MOTION_EVENT_SOURCES; static { VALID_USER_AUTH_TYPES = new SparseArray<>(5); VALID_USER_AUTH_TYPES.put(KEY_FOB, "KEY_FOB"); VALID_USER_AUTH_TYPES.put(CUSTOM_1, "CUSTOM_1"); VALID_USER_AUTH_TYPES.put(CUSTOM_2, "CUSTOM_2"); VALID_USER_AUTH_TYPES.put(CUSTOM_3, "CUSTOM_3"); VALID_USER_AUTH_TYPES.put(CUSTOM_4, "CUSTOM_4"); VALID_USER_AUTH_TYPES_HELP = getHelpString("types", VALID_USER_AUTH_TYPES); VALID_USER_AUTH_SET_VALUES = new SparseArray<>(3); VALID_USER_AUTH_SET_VALUES.put(ASSOCIATE_CURRENT_USER, "ASSOCIATE_CURRENT_USER"); VALID_USER_AUTH_SET_VALUES.put(DISASSOCIATE_CURRENT_USER, "DISASSOCIATE_CURRENT_USER"); VALID_USER_AUTH_SET_VALUES.put(DISASSOCIATE_ALL_USERS, "DISASSOCIATE_ALL_USERS"); VALID_USER_AUTH_SET_VALUES_HELP = getHelpString("values", VALID_USER_AUTH_SET_VALUES); CUSTOM_INPUT_FUNCTION_ARGS = new ArrayMap<>(10); CUSTOM_INPUT_FUNCTION_ARGS.put("f1", CustomInputEvent.INPUT_CODE_F1); CUSTOM_INPUT_FUNCTION_ARGS.put("f2", CustomInputEvent.INPUT_CODE_F2); CUSTOM_INPUT_FUNCTION_ARGS.put("f3", CustomInputEvent.INPUT_CODE_F3); CUSTOM_INPUT_FUNCTION_ARGS.put("f4", CustomInputEvent.INPUT_CODE_F4); CUSTOM_INPUT_FUNCTION_ARGS.put("f5", CustomInputEvent.INPUT_CODE_F5); CUSTOM_INPUT_FUNCTION_ARGS.put("f6", CustomInputEvent.INPUT_CODE_F6); CUSTOM_INPUT_FUNCTION_ARGS.put("f7", CustomInputEvent.INPUT_CODE_F7); CUSTOM_INPUT_FUNCTION_ARGS.put("f8", CustomInputEvent.INPUT_CODE_F8); CUSTOM_INPUT_FUNCTION_ARGS.put("f9", CustomInputEvent.INPUT_CODE_F9); CUSTOM_INPUT_FUNCTION_ARGS.put("f10", CustomInputEvent.INPUT_CODE_F10); MOTION_EVENT_SOURCES = new ArrayMap<>(10); MOTION_EVENT_SOURCES.put("keyboard", InputDevice.SOURCE_KEYBOARD); MOTION_EVENT_SOURCES.put("dpad", InputDevice.SOURCE_DPAD); MOTION_EVENT_SOURCES.put("gamepad", InputDevice.SOURCE_GAMEPAD); MOTION_EVENT_SOURCES.put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN); MOTION_EVENT_SOURCES.put("mouse", InputDevice.SOURCE_MOUSE); MOTION_EVENT_SOURCES.put("stylus", InputDevice.SOURCE_STYLUS); MOTION_EVENT_SOURCES.put("trackball", InputDevice.SOURCE_TRACKBALL); MOTION_EVENT_SOURCES.put("touchpad", InputDevice.SOURCE_TOUCHPAD); MOTION_EVENT_SOURCES.put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION); MOTION_EVENT_SOURCES.put("joystick", InputDevice.SOURCE_JOYSTICK); } // CarTelemetryManager may not send result back if there are not results for the given // metrics config. private static final Duration TELEMETRY_RESULT_WAIT_TIMEOUT = Duration.ofSeconds(5); private static String getHelpString(String name, SparseArray values) { StringBuilder help = new StringBuilder("Valid ").append(name).append(" are: "); int size = values.size(); for (int i = 0; i < size; i++) { help.append(values.valueAt(i)); if (i != size - 1) { help.append(", "); } } return help.append('.').toString(); } private final Context mContext; private final VehicleHal mHal; private final CarAudioService mCarAudioService; private final CarPackageManagerService mCarPackageManagerService; private final CarProjectionService mCarProjectionService; private final CarPowerManagementService mCarPowerManagementService; private final FixedActivityService mFixedActivityService; private final CarFeatureController mFeatureController; private final CarInputService mCarInputService; private final CarNightService mCarNightService; private final SystemInterface mSystemInterface; private final GarageModeService mGarageModeService; private final CarUserService mCarUserService; private final CarOccupantZoneService mCarOccupantZoneService; private final CarEvsService mCarEvsService; private final CarWatchdogService mCarWatchdogService; private final CarTelemetryService mCarTelemetryService; private final CarUxRestrictionsManagerService mCarUxRestrictionsManagerService; private final Map mAllServicesByClazz; private long mKeyDownTime; private long mMotionDownTime; private ServiceConnection mScriptExecutorConn; private IScriptExecutor mScriptExecutor; private AudioZoneMirrorStatusCallbackImpl mMirrorStatusCallback; CarShellCommand(Context context, VehicleHal hal, CarFeatureController featureController, SystemInterface systemInterface, Map allServicesByClazz) { mContext = context; mHal = hal; mFeatureController = featureController; mSystemInterface = systemInterface; mCarAudioService = (CarAudioService) allServicesByClazz.get(CarAudioService.class); mCarPackageManagerService = (CarPackageManagerService) allServicesByClazz.get( CarPackageManagerService.class); mCarProjectionService = (CarProjectionService) allServicesByClazz.get( CarProjectionService.class); mCarPowerManagementService = (CarPowerManagementService) allServicesByClazz.get( CarPowerManagementService.class); mFixedActivityService = (FixedActivityService) allServicesByClazz.get( FixedActivityService.class); mCarInputService = (CarInputService) allServicesByClazz.get(CarInputService.class); mCarNightService = (CarNightService) allServicesByClazz.get(CarNightService.class); mGarageModeService = (GarageModeService) allServicesByClazz.get(GarageModeService.class); mCarUserService = (CarUserService) allServicesByClazz.get(CarUserService.class); mCarOccupantZoneService = (CarOccupantZoneService) allServicesByClazz.get(CarOccupantZoneService.class); mCarEvsService = (CarEvsService) allServicesByClazz.get(CarEvsService.class); mCarWatchdogService = (CarWatchdogService) allServicesByClazz.get(CarWatchdogService.class); mCarTelemetryService = (CarTelemetryService) allServicesByClazz.get(CarTelemetryService.class); mCarUxRestrictionsManagerService = (CarUxRestrictionsManagerService) allServicesByClazz.get(CarUxRestrictionsManagerService.class); mAllServicesByClazz = allServicesByClazz; } @Override public int onCommand(String cmd) { if (cmd == null) { onHelp(); return RESULT_ERROR; } ArrayList argsList = new ArrayList<>(); argsList.add(cmd); String arg = null; do { arg = getNextArg(); if (arg != null) { argsList.add(arg); } } while (arg != null); String[] args = new String[argsList.size()]; argsList.toArray(args); try (IndentingPrintWriter pw = new IndentingPrintWriter(getOutPrintWriter())) { return exec(args, pw); } } @Override public void onHelp() { try (IndentingPrintWriter pw = new IndentingPrintWriter(getOutPrintWriter())) { showHelp(pw); } } private static void showHelp(IndentingPrintWriter pw) { pw.println("Car service commands:"); pw.println("\t-h"); pw.println("\t Print this help text."); pw.println("\tday-night-mode [day|night|sensor]"); pw.println("\t Force into day/night mode or restore to auto."); pw.println("\tinject-vhal-event [area ID] data(can be comma separated list) [-t delay_time_seconds]"); pw.println("\t Inject a vehicle property for testing."); pw.println("\t delay_time_seconds: the event timestamp is increased by certain second."); pw.println("\t If not specified, it will be 0."); pw.println("\tinject-error-event zone "); pw.println("\t Inject an error event from VHAL for testing."); pw.println("\tinject-continuous-events " + "data(can be comma separated list) " + "[-z zone] [-s SampleRate in Hz] [-d time duration in seconds]"); pw.println("\t Inject continuous vehicle events for testing."); pw.printf("\t If not specified, CarService will inject fake events with areaId:%s " + "at sample rate %s for %s seconds.", PARAM_VEHICLE_PROPERTY_GLOBAL_AREA_ID, PARAM_INJECT_EVENT_DEFAULT_RATE, PARAM_INJECT_EVENT_DEFAULT_DURATION); pw.println("\tenable-uxr true|false"); pw.println("\t Enable/Disable UX restrictions and App blocking."); 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 [property name in SCREAMING_SNAKE_CASE or ID in Hex or" + " Decimal]"); pw.println("\t Get a specific CarPropertyConfig or list all CarPropertyConfigs"); pw.println("\tget-property-value [property name in SCREAMING_SNAKE_CASE or ID in Hex or " + "Decimal] [areaId]"); pw.println("\t Get a vehicle property value by property id and areaId"); pw.println("\t or list all property values for all areaId"); pw.printf("\t%s\n", getSetPropertyValueUsage()); pw.printf("\t%s\n", getSuspendCommandUsage(COMMAND_SUSPEND)); pw.println("\t Suspend the system to RAM."); pw.printf("\t %s forces the device to perform suspend-to-RAM.\n", PARAM_REAL); pw.printf("\t %s simulates suspend-to-RAM instead of putting the device into deep sleep." + "\n", PARAM_SIMULATE); pw.printf("\t %s depending on the device capability, real or simulated suspend-to-RAM is " + "performed.\n", PARAM_AUTO); pw.printf("\t %s skips Garage Mode before going into sleep.\n", PARAM_SKIP_GARAGEMODE); pw.printf("\t %s [RESUME_DELAY] wakes up the device RESUME_DELAY seconds after suspend.\n", PARAM_WAKEUP_AFTER); pw.printf("\t %s [RESUME_DELAY] cancels the wake up after RESUME_DELAY seconds, if this" + " flag is set, device will not go into suspend mode and wait in shutdown" + " prepare for RESUME_DELAY seconds.\n", PARAM_CANCEL_AFTER); pw.printf("\t%s\n", getSuspendCommandUsage(COMMAND_HIBERNATE)); pw.println("\t Suspend the system to disk."); pw.printf("\t %s forces the device to perform suspend-to-disk.\n", PARAM_REAL); pw.printf("\t %s simulates suspend-to-disk instead of putting the device into " + "hibernation.\n", PARAM_SIMULATE); pw.printf("\t %s depending on the device capability, real or simulated suspend-to-disk is " + "performed.\n", PARAM_AUTO); pw.printf("\t %s skips Garage Mode before going into hibernation.\n", PARAM_SKIP_GARAGEMODE); pw.printf("\t %s frees cached apps memory before simulating hibernation.\n", PARAM_FREE_MEMORY); pw.println("\tresume"); pw.println("\t Wake the system up after a simulated suspension/hibernation."); pw.println("\tset-display-state [displayId] [true|false]"); pw.println("\t Turn on or off the individual display."); pw.println("\tprojection-tethering [true|false]"); pw.println("\t Whether tethering should be used when creating access point for" + " wireless projection"); pw.println("\t--metrics"); pw.println("\t When used with dumpsys, only metrics will be in the dumpsys output."); pw.printf("\t%s [zoneid] [uid]\n", COMMAND_SET_UID_TO_ZONE); pw.println("\t Maps the audio zoneid to uid."); pw.printf("\t%s\n", COMMAND_RESET_VOLUME_CONTEXT); pw.println("\t Resets the last selected volume context for volume changes."); pw.printf("\t%s [zoneId] [groupId] [%s\\%s]\n", COMMAND_SET_MUTE_CAR_VOLUME_GROUP, PARAM_MUTE, PARAM_UNMUTE); pw.printf("\t %s\\%s groupId in zoneId\n", PARAM_MUTE, PARAM_UNMUTE); pw.printf("\t%s [zoneId] [groupId] [volume]\n", COMMAND_SET_GROUP_VOLUME); pw.println("\t sets the group volume for [groupId] in [zoneId] to %volume,"); pw.println("\t [volume] must be an integer between 0 to 100"); pw.printf("\t%s [zoneId1] [zoneId2]\n", COMMAND_SET_AUDIO_MIRROR); pw.println("\t sets audio mirror for zones [zoneId1] and [zoneId2],"); pw.println("\t [zoneId#] must be a valid zone id "); pw.printf("\t%s [value] [--requestId]\n", COMMAND_UNSET_AUDIO_MIRROR); pw.println("\t unsets audio mirror for zone [value],"); pw.println("\t [value] must be a valid zone id"); pw.println("\t use --requestId to disable a request id instead"); 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"); pw.println("\t Stop fixed Activity mode for the given display. " + "The Activity will not be restarted upon crash."); pw.println("\tenable-feature featureName"); pw.println("\t Enable the requested feature. Change will happen after reboot."); pw.println("\t This requires root/su."); pw.println("\tdisable-feature featureName"); pw.println("\t Disable the requested feature. Change will happen after reboot"); pw.println("\t This requires root/su."); pw.println("\tinject-key [-d display] [-s seat] [-t down_delay_ms | -a down|up] key_code"); pw.println("\t inject key down and/or up event to car service"); pw.println("\t display: 0 for main, 1 for cluster. If not specified, it will be 0."); pw.println("\t seat: int seat value defined in VeihicleAreaSeat. 0 for unknown,"); pw.println("\t 0x0001 for row1 left, 0x0002 for row1 center, 0x0004 for row1 right,"); pw.println("\t 0x0010 for row2 left, 0x0020 for row2 center, 0x0040 for row2 right,"); pw.println("\t 0x0100 for row3 left, 0x0200 for row3 center, 0x0400 for row3 right."); pw.println("\t If not specified, it will be driver seat."); pw.println("\t down_delay_ms: delay from down to up key event. If not specified,"); pw.println("\t it will be 0"); pw.println("\t key_code: int key code defined in android KeyEvent"); pw.println("\t If -a isn't specified, both down and up will be injected."); pw.println("\tinject-motion [-d display] [-s seat] [--source source] [-t down_delay_ms] " + "[-a action] [-c count] [-p pointer_id0 pointer_id1...] x0 y0 x1 y1 ..."); pw.println("\t inject motion down or up or move or cancel event to car service"); pw.println("\t display: 0 for main, 1 for cluster. If not specified, it will be 0."); pw.println("\t seat: int seat value defined in VeihicleAreaSeat. 0 for unknown,"); pw.println("\t 0x0001 for row1 left, 0x0002 for row1 center, 0x0004 for row1 right,"); pw.println("\t 0x0010 for row2 left, 0x0020 for row2 center, 0x0040 for row2 right,"); pw.println("\t 0x0100 for row3 left, 0x0200 for row3 center, 0x0400 for row3 right."); pw.println("\t If not specified, it will be driver seat."); pw.println("\t source: string source value for motion event."); pw.println("\t If not specified, it will be touchscreen"); pw.println("\t The sources are:"); pw.println("\t touchnavigation"); pw.println("\t touchscreen"); pw.println("\t joystick"); pw.println("\t stylus"); pw.println("\t touchpad"); pw.println("\t gamepad"); pw.println("\t dpad"); pw.println("\t mouse"); pw.println("\t keyboard"); pw.println("\t trackball"); pw.println("\t down_delay_ms: delay from down to up motion event. If not specified,"); pw.println("\t it will be 0"); pw.println("\t action: the MotionEvent.ACTION_* for the event. If not specified,"); pw.println("\t both down and up will be injected."); pw.println("\t This can be a string value that is down|up|move|cancel"); pw.println("\t or an integer value that must equal to MotionEvent.ACTION_*."); pw.println("\t count: the count of pointers. If not specified, it will be 1."); pw.println("\t If this value is greater than 1, there must be as many pointer_id,"); pw.println("\t x, y as this value."); pw.println("\t pointer_id: pointer ids of following coordinates, If not specified,"); pw.println("\t they are automatically set in order from 0."); pw.println("\t x: int x coordinate in android MotionEvent"); pw.println("\t y: int y coordinate in android MotionEvent"); pw.println("\t Must provide the 'count' number of x, y pairs."); pw.println("\tinject-rotary [-d display] [-i input_type] [-c clockwise]"); pw.println("\t [-dt delta_times_ms]"); pw.println("\t inject rotary input event to car service."); pw.println("\t display: 0 for main, 1 for cluster. If not specified, it will be 0."); pw.println("\t input_type: 10 for navigation controller input, 11 for volume"); pw.println("\t controller input. If not specified, it will be 10."); pw.println("\t clockwise: true if the event is clockwise, false if the event is"); pw.println("\t counter-clockwise. If not specified, it will be false."); pw.println("\t delta_times_ms: a list of delta time (current time minus event time)"); pw.println("\t in descending order. If not specified, it will be 0."); pw.println("\tinject-custom-input [-d display] [-r repeatCounter] EVENT"); pw.println("\t display: 0 for main, 1 for cluster. If not specified, it will be 0."); pw.println("\t repeatCounter: number of times the button was hit (default value is 1)"); pw.println("\t EVENT: mandatory last argument. Possible values for for this flag are "); pw.println("\t F1, F2, up to F10 (functions to defined by OEM partners)"); pw.printf("\t%s [--timeout TIMEOUT_MS]\n", COMMAND_GET_INITIAL_USER_INFO); pw.println("\t Calls the Vehicle HAL to get the initial boot info, passing the given"); pw.println("\t REQ_TYPE (which could be either FIRST_BOOT, FIRST_BOOT_AFTER_OTA, "); pw.println("\t COLD_BOOT, RESUME, or any numeric value that would be passed 'as-is')"); pw.println("\t and an optional TIMEOUT_MS to wait for the HAL response (if not set,"); pw.println("\t it will use a default value)."); pw.println("\t The --hal-only option only calls HAL, without using CarUserService."); pw.printf("\t%s [--hal-only] [--ignore-uxr] [--timeout TIMEOUT_MS]\n", COMMAND_SWITCH_USER); pw.println("\t Switches to user USER_ID using the HAL integration."); pw.println("\t The --hal-only option only calls HAL, without switching the user,"); pw.println("\t The --ignore-uxr option ignores any Ux restriction regarding user switch,"); pw.println("\t while the --timeout defines how long to wait for the response."); pw.printf("\t%s [--timeout TIMEOUT_MS]\n", COMMAND_LOGOUT_USER); pw.println("\t Logout the current user (if the user was switched toby a device admin)."); pw.println("\t The --timeout option defines how long to wait for the UserHal response."); pw.printf("\t%s [--hal-only]\n", COMMAND_REMOVE_USER); pw.println("\t Removes user with USER_ID using the HAL integration."); pw.println("\t The --hal-only option only calls HAL, without removing the user,"); pw.printf("\t%s [--hal-only] [--timeout TIMEOUT_MS] [--guest] [--flags FLAGS] [NAME]\n", COMMAND_CREATE_USER); pw.println("\t Creates a new user using the HAL integration."); pw.println("\t The --hal-only uses UserManager to create the user,"); pw.println("\t while the --timeout defines how long to wait for the response."); pw.printf("\t%s\n", COMMAND_GET_INITIAL_USER); pw.printf("\t Gets the id of the initial user (or %s when it's not available)\n", NO_INITIAL_USER); pw.printf("\t%s [occupantZoneId] [userId]\n", COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE); pw.println("\t Maps the occupant zone id to user id."); pw.printf("\t%s [occupantZoneId]\n", COMMAND_RESET_USER_ID_IN_OCCUPANT_ZONE); pw.println("\t Unmaps the user assigned to occupant zone id."); pw.printf("\t%s [--hal-only] [--user USER_ID] TYPE1 [..TYPE_N]\n", COMMAND_GET_USER_AUTH_ASSOCIATION); pw.println("\t Gets the N user authentication values for the N types for the given user"); pw.println("\t (or current user when not specified)."); pw.println("\t By defautt it calls CarUserManager, but using --hal-only will call just " + "UserHalService."); pw.printf("\t%s [--hal-only] [--user USER_ID] TYPE1 VALUE1 [..TYPE_N VALUE_N]\n", COMMAND_SET_USER_AUTH_ASSOCIATION); pw.println("\t Sets the N user authentication types with the N values for the given user"); pw.println("\t (or current user when not specified)."); pw.println("\t By default it calls CarUserManager, but using --hal-only will call just " + "UserHalService."); pw.printf("\t %s\n", VALID_USER_AUTH_TYPES_HELP); pw.printf("\t %s\n", VALID_USER_AUTH_SET_VALUES_HELP); pw.printf("\t%s [true|false]\n", COMMAND_SET_START_BG_USERS_ON_GARAGE_MODE); pw.println("\t Controls backgroud user start and stop during garage mode."); pw.println("\t If false, garage mode operations (background users start at garage mode" + " entry and background users stop at garage mode exit) will be skipped."); pw.printf("\t %s [%s|%s|%s|%s]\n", COMMAND_SILENT_MODE, SILENT_MODE_FORCED_SILENT, SILENT_MODE_FORCED_NON_SILENT, SILENT_MODE_NON_FORCED, PARAM_QUERY_MODE); pw.println("\t Forces silent mode silent or non-silent. With query (or no command) " + "displays the silent state"); pw.println("\t and shows how many listeners are monitoring the state."); pw.printf("\t%s [%s|%s|%s|%s]\n", COMMAND_EMULATE_DRIVING_STATE, DRIVING_STATE_DRIVE, DRIVING_STATE_PARK, DRIVING_STATE_REVERSE, DRIVING_STATE_NEUTRAL); pw.println("\t Emulates the giving driving state."); pw.printf("\t%s [--enable COMP1,COMP2,...] [--disable COMP1,COMP2,...]\n", COMMAND_DEFINE_POWER_POLICY); pw.println("\t Defines a power policy. Components not specified in --enable or --disable"); pw.println("\t are unchanged when the policy is applied."); pw.println("\t Components should be comma-separated without space."); pw.printf("\t%s \n", COMMAND_APPLY_POWER_POLICY); pw.println("\t Applies power policy which is defined in " + "/vendor/etc/automotive/power_policy.xml or"); pw.printf("\t by %s command\n", COMMAND_DEFINE_POWER_POLICY); pw.printf("\t%s [%s:] [%s:]\n", COMMAND_DEFINE_POWER_POLICY_GROUP, POWER_STATE_WAIT_FOR_VHAL, POWER_STATE_ON); pw.println("\t Defines a power policy group. The policy ID must be defined in advance."); pw.printf("\t%s \n", COMMAND_SET_POWER_POLICY_GROUP); pw.println("\t Sets power policy group which is defined in " + "/vendor/etc/automotive/power_policy.xml "); pw.printf("\t or by %s command\n", COMMAND_DEFINE_POWER_POLICY_GROUP); pw.printf("\t%s\n", COMMAND_APPLY_CTS_VERIFIER_POWER_OFF_POLICY); pw.println("\t Define and apply the cts_verifier_off power policy with " + "--disable WIFI,LOCATION,BLUETOOTH"); pw.printf("\t%s\n", COMMAND_APPLY_CTS_VERIFIER_POWER_ON_POLICY); pw.println("\t Define and apply the cts_verifier_on power policy with " + "--enable WIFI,LOCATION,BLUETOOTH"); pw.printf("\t%s [%s] [%s]\n", COMMAND_POWER_OFF, PARAM_SKIP_GARAGEMODE, PARAM_REBOOT); pw.println("\t Powers off the car."); pw.printf("\t%s \n", COMMAND_SET_REARVIEW_CAMERA_ID); pw.println("\t Configures a target camera device CarEvsService to use."); pw.println("\t If CAMEAR_ID is \"default\", this command will configure CarEvsService "); pw.println("\t to use its default camera device."); pw.printf("\t%s\n", COMMAND_GET_REARVIEW_CAMERA_ID); pw.println("\t Gets the name of the camera device CarEvsService is using for " + "the rearview."); pw.printf("\t%s \n", COMMAND_SET_CAMERA_ID); pw.println("\t Configures a target camera device CarEvsService will use for a specified "); pw.println("\t service type."); pw.println("\t Possible SERVICE_TYPEs are REARVIEW, FRONTVIEW, LEFTVIEW, RIGHTVIEW, "); pw.println("\t DRIVERVIEW, FRONT_PASSENGERSVIEW, REAR_PASSENGERSVIEW, or USER_DEFINED"); pw.println("\t (* of CarEvsManager.SERVICE_TYPE_* to specify a service type)."); pw.printf("\t%s \n", COMMAND_GET_CAMERA_ID); pw.println("\t Gets the name of the camera device that is assigned to a specified "); pw.println("\t service type."); pw.println("\t Possible SERVICE_TYPEs are REARVIEW, FRONTVIEW, LEFTVIEW, RIGHTVIEW, "); pw.println("\t DRIVERVIEW, FRONT_PASSENGERSVIEW, REAR_PASSENGERSVIEW, or USER_DEFINED"); pw.println("\t (* of CarEvsManager.SERVICE_TYPE_* to specify a service type)."); pw.printf("\t%s \n", COMMAND_ENABLE_CAMERA_SERVICE_TYPE); pw.println("\t Enables a specified service type with a camera associated with a given "); pw.println("\t camera id."); pw.println("\t Use * of CarEvsManager.SERVICE_TYPE_* to specify a service type."); pw.printf("\t%s \n", COMMAND_CHECK_CAMERA_SERVICE_TYPE_ENABLED); pw.println("\t Checks whether or not a given service type is enabled."); pw.println("\t Use * of CarEvsManager.SERVICE_TYPE_* to specify a service type."); pw.printf("\t%s true|false \n", COMMAND_WATCHDOG_CONTROL_PACKAGE_KILLABLE_STATE); pw.println("\t Marks PACKAGE_NAME as killable or not killable on resource overuse "); pw.printf("\t%s \n", COMMAND_WATCHDOG_IO_SET_3P_FOREGROUND_BYTES); pw.println("\t Sets third-party apps foreground I/O overuse threshold"); pw.printf("\t%s\n", COMMAND_WATCHDOG_IO_GET_3P_FOREGROUND_BYTES); pw.println("\t Gets third-party apps foreground I/O overuse threshold"); pw.printf("\t%s enable|disable\n", COMMAND_WATCHDOG_CONTROL_PROCESS_HEALTH_CHECK); pw.println("\t Enables/disables car watchdog process health check."); pw.printf("\t%s [--user USER_ID]\n", COMMAND_WATCHDOG_RESOURCE_OVERUSE_KILL); pw.println("\t Kills PACKAGE_NAME due to resource overuse."); pw.printf("\t%s [REGION_STRING]", COMMAND_DRIVING_SAFETY_SET_REGION); pw.println("\t Set driving safety region."); pw.println("\t Skipping REGION_STRING leads into resetting to all regions"); pw.printf("\t%s ", COMMAND_TELEMETRY); pw.println("\t Telemetry commands."); pw.println("\t Provide -h to see the list of sub-commands."); pw.printf("\t%s get|default|enable|disable_until_used \n", COMMAND_CONTROL_COMPONENT_ENABLED_STATE); pw.println("\t Gets the current EnabledState, or changes the Application EnabledState" + " to DEFAULT, ENABLED or DISABLED_UNTIL_USED."); pw.printf("\t%s [user]\n", COMMAND_CHECK_LOCK_IS_SECURE); pw.println("\t check if the current or given user has a lock to secure"); pw.printf("\t%s", COMMAND_LIST_VHAL_PROPS); pw.println("\t list all supported property IDS by vehicle HAL"); pw.printf("\t%s", COMMAND_GET_VHAL_BACKEND); pw.println("\t list whether we are connected to AIDL or HIDL vehicle HAL backend"); pw.printf("\t%s ", COMMAND_TEST_ECHO_REVERSE_BYTES); pw.println("\t test the ECHO_REVERSE_BYTES property. PROP_ID is the ID (int) for " + "ECHO_REVERSE_BYTES, REQUEST_SIZE is how many byteValues in the request. " + "This command can be used for testing LargeParcelable by passing large request."); pw.printf("\t%s [--user USER] [APPN]", COMMAND_GET_TARGET_CAR_VERSION); pw.println("\t Gets the target API version (major and minor) defined by the given apps " + "for the given user (or current user when --user is not set)."); pw.printf("\t%s ", COMMAND_SET_PROCESS_GROUP); pw.println("\t Change CPU group of a process. Check android.os.Process.setProcessGroup " + "for details on the parameters."); pw.printf("\t%s ", COMMAND_GET_PROCESS_GROUP); pw.println("\t Get the CPU group of a process. Check android.os.Process.getProcessGroup " + "for details on the parameters."); pw.printf("\t%s ", COMMAND_SET_PROCESS_PROFILE); pw.println("\t Change CPU profile (=CPUSet) of a process. Check " + "android.os.Process.setProcessProfile for details on the parameters."); pw.printf("\t%s ", COMMAND_GET_DISPLAY_BY_USER); pw.println("\t Gets the display associated to the given user"); pw.printf("\t%s ", COMMAND_GET_USER_BY_DISPLAY); pw.println("\t Gets the user associated with the given display"); pw.printf("\t%s ", COMMAND_GET_CURRENT_UX_RESTRICTIONS); pw.println("\t Gets the current UX restriction on given display. If no display is " + "provided, return current UX restrictions on default display."); pw.printf("\t%s ", COMMAND_SET_CURRENT_UXR_MODE); pw.println("\t Sets current mode for UX restrictions."); pw.printf("\t%s", COMMAND_GET_CURRENT_UXR_MODE); pw.println("\t Gets current mode for UX restrictions."); pw.printf("\t%s", COMMAND_GET_SUPPORTED_UXR_MODES); pw.println("\t Gets all supported UX restrictions modes."); pw.printf("\t%s", COMMAND_GET_UXR_CONFIG); pw.println("\t Gets UX restrictions configuration."); pw.printf("\t%s", COMMAND_GET_INPUT_AND_DISPLAY_INFO); pw.println("\t Gets input devices & their descriptor; and gets display devices & their " + "uniqueId"); pw.printf("\t%s ", COMMAND_ADD_INPUT_DESCRIPTOR_ASSOCIATION_TO_DISPLAY_UNIQUE_ID); pw.println("\t Add association of the input device to the particular display by using " + "input descriptor."); pw.printf("\t%s ", COMMAND_REMOVE_INPUT_DESCRIPTOR_ASSOCIATION); pw.println("\t Remove association of the input device descriptor to any display."); } private static int showInvalidArguments(IndentingPrintWriter pw) { pw.println("Incorrect number of arguments."); showHelp(pw); return RESULT_ERROR; } private void runSetZoneIdForUid(String zoneString, String uidString) { int uid = Integer.parseInt(uidString); int zoneId = Integer.parseInt(zoneString); mCarAudioService.setZoneIdForUid(zoneId, uid); } private void runSetMuteCarVolumeGroup(String zoneString, String groupIdString, String muteString) { int groupId = Integer.parseInt(groupIdString); int zoneId = Integer.parseInt(zoneString); if (!PARAM_MUTE.equalsIgnoreCase(muteString) && !PARAM_UNMUTE.equalsIgnoreCase(muteString)) { throw new IllegalArgumentException("Failed to set volume group mute for " + groupIdString + " in zone " + zoneString + ", bad mute argument: " + muteString); } boolean muteState = PARAM_MUTE.equalsIgnoreCase(muteString); mCarAudioService.setVolumeGroupMute(zoneId, groupId, muteState, FLAG_SHOW_UI); } private void runSetGroupVolume(String zoneIdString, String groupIdString, String volumeString) { int groupId = Integer.parseInt(groupIdString); int zoneId = Integer.parseInt(zoneIdString); int percentVolume = Integer.parseInt(volumeString); Preconditions.checkArgumentInRange(percentVolume, 0, 100, "%volume for group " + groupIdString + " in zone " + zoneIdString); int minIndex = mCarAudioService.getGroupMinVolume(zoneId, groupId); int maxIndex = mCarAudioService.getGroupMaxVolume(zoneId, groupId); int index = minIndex + (int) ((float) (maxIndex - minIndex) * ((float) percentVolume / 100.0f)); mCarAudioService.setGroupVolume(zoneId, groupId, index, FLAG_SHOW_UI); } private void runSetAudioMirror(String zoneId1String, String zoneId2String, IndentingPrintWriter writer) { int zoneId1 = Integer.parseInt(zoneId1String); int zoneId2 = Integer.parseInt(zoneId2String); if (mMirrorStatusCallback == null) { mMirrorStatusCallback = new AudioZoneMirrorStatusCallbackImpl(); boolean registered = mCarAudioService.registerAudioZonesMirrorStatusCallback( mMirrorStatusCallback); if (!registered) { writer.printf("Could not register audio mirror status callback for zones %s %s\n", zoneId1String, zoneId2String); mMirrorStatusCallback = null; return; } } mMirrorStatusCallback.reset(); mCarAudioService.enableMirrorForAudioZones(new int[] {zoneId1, zoneId2}); boolean called; try { called = mMirrorStatusCallback.waitForCallback(); } catch (Exception e) { Slogf.e(TAG, e, "runSetAudioMirror wait for callback failed for zones %s %s", zoneId1String, zoneId2String); return; } if (!called) { writer.printf("Did not receive mirror status callback for zones %s %s\n", zoneId1String, zoneId2String); return; } writer.printf("Received mirror status callback for zones %s %s\n", zoneId1String, zoneId2String); writer.increaseIndent(); for (int c = 0; c < mMirrorStatusCallback.mZoneIds.length; c++) { writer.printf("Received zone[%d] %d\n", c , mMirrorStatusCallback.mZoneIds[c]); } writer.printf("Received status %d\n", mMirrorStatusCallback.mStatus); writer.decreaseIndent(); } private void runUnsetAudioMirror(String[] args, IndentingPrintWriter writer) { boolean useConfig = false; for (int i = 2; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--requestId": useConfig = true; break; default: writer.println("Invalid option at index " + i + ": " + arg); return; } } String inputString = args[1]; if (mMirrorStatusCallback == null) { mMirrorStatusCallback = new AudioZoneMirrorStatusCallbackImpl(); boolean registered = mCarAudioService.registerAudioZonesMirrorStatusCallback( mMirrorStatusCallback); if (!registered) { mMirrorStatusCallback = null; return; } } mMirrorStatusCallback.reset(); if (useConfig) { long requestId = Long.parseLong(inputString); mCarAudioService.disableAudioMirror(requestId); } else { int zoneId = Integer.parseInt(inputString); mCarAudioService.disableAudioMirrorForZone(zoneId); } boolean called; try { called = mMirrorStatusCallback.waitForCallback(); } catch (Exception e) { Slogf.e(TAG, e, "runUnsetAudioMirror wait for callback failed for zones %s", inputString); return; } if (!called) { writer.printf("Did not receive mirror status callback for zones %s\n", inputString); return; } writer.printf("Received mirror status callback for zones %s\n", inputString); writer.increaseIndent(); for (int c = 0; c < mMirrorStatusCallback.mZoneIds.length; c++) { writer.printf("Received zone[%d] %d\n", c , mMirrorStatusCallback.mZoneIds[c]); } writer.printf("Received status %d\n", mMirrorStatusCallback.mStatus); writer.decreaseIndent(); mCarAudioService.unregisterAudioZonesMirrorStatusCallback(mMirrorStatusCallback); } private void runResetSelectedVolumeContext() { mCarAudioService.resetSelectedVolumeContext(); } private void runSetOccupantZoneIdForUserId(String occupantZoneIdString, String userIdString) { int userId = Integer.parseInt(userIdString); int occupantZoneId = Integer.parseInt(occupantZoneIdString); if (!mCarOccupantZoneService.assignProfileUserToOccupantZone(occupantZoneId, userId)) { throw new IllegalStateException("Failed to set userId " + userId + " to occupantZoneId " + occupantZoneIdString); } } private void runResetOccupantZoneId(String occupantZoneIdString) { int occupantZoneId = Integer.parseInt(occupantZoneIdString); if (!mCarOccupantZoneService .assignProfileUserToOccupantZone(occupantZoneId, UserManagerHelper.USER_NULL)) { throw new IllegalStateException("Failed to reset occupantZoneId " + occupantZoneIdString); } } private void assertHasAtLeastOnePermission(String cmd, String[] requiredPermissions) { for (String requiredPermission : requiredPermissions) { if (CarServiceUtils.hasPermission(mContext, requiredPermission)) return; } if (requiredPermissions.length == 1) { throw new SecurityException("The command '" + cmd + "' requires permission:" + requiredPermissions[0]); } throw new SecurityException( "The command " + cmd + " requires one of the following permissions:" + Arrays.toString(requiredPermissions)); } int exec(String[] args, IndentingPrintWriter writer) { String cmd = args[0]; String[] requiredPermissions = USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.get(cmd); if (requiredPermissions == null) { String requiredPermission = USER_BUILD_COMMAND_TO_PERMISSION_MAP.get(cmd); if (requiredPermission != null) { requiredPermissions = new String[] { requiredPermission }; } } if (VERBOSE) { Slogf.v(TAG, "cmd: " + cmd + ", requiredPermissions: " + Arrays.toString(requiredPermissions)); } if (BuildHelper.isUserBuild() && requiredPermissions == null) { throw new SecurityException("The command '" + cmd + "' requires non-user build"); } if (requiredPermissions != null) { assertHasAtLeastOnePermission(cmd, requiredPermissions); } switch (cmd) { case COMMAND_HELP: showHelp(writer); break; case COMMAND_DAY_NIGHT_MODE: { String value = args.length < 2 ? "" : args[1]; forceDayNightMode(value, writer); break; } case COMMAND_GARAGE_MODE: { String value = args.length < 2 ? "" : args[1]; forceGarageMode(value, writer); break; } case COMMAND_INJECT_VHAL_EVENT: injectVhalEvent(args, writer); break; case COMMAND_INJECT_CONTINUOUS_EVENT: injectContinuousEvents(args, writer); break; case COMMAND_INJECT_ERROR_EVENT: injectErrorEvent(args, writer); break; case COMMAND_ENABLE_UXR: if (args.length != 2) { return showInvalidArguments(writer); } boolean enableBlocking = Boolean.valueOf(args[1]); if (mCarPackageManagerService != null) { // Need to clear the binder identity if calling process is root since // signature will not match with that of car service. final long identity = Binder.clearCallingIdentity(); try { mCarPackageManagerService.setEnableActivityBlocking(enableBlocking); } finally { Binder.restoreCallingIdentity(identity); } } break; case COMMAND_GET_DO_ACTIVITIES: if (args.length != 2) { return showInvalidArguments(writer); } String pkgName = args[1].toLowerCase(Locale.US); if (mCarPackageManagerService != null) { String[] doActivities = mCarPackageManagerService.getDistractionOptimizedActivities( pkgName); if (doActivities != null) { writer.println("DO Activities for " + pkgName); for (String a : doActivities) { writer.println(a); } } else { writer.println("No DO Activities for " + pkgName); } } break; case COMMAND_GET_CARPROPERTYCONFIG: getCarPropertyConfig(args, writer); break; case COMMAND_GET_PROPERTY_VALUE: getPropertyValue(args, writer); break; case COMMAND_GENERATE_TEST_VENDOR_CONFIGS: try (CarTestService.NativePipe pipe = CarTestService.NativePipe.newPipe()) { mHal.dumpVhal(pipe.getFileDescriptor(), List.of("--genTestVendorConfigs")); writer.print(pipe.getOutput(DEFAULT_HAL_TIMEOUT_MS)); } catch (Exception e) { Slogf.w(TAG, "dumpVhal --genTestVendorConfigs Failed", e); return showInvalidArguments(writer); } break; case COMMAND_RESTORE_TEST_VENDOR_CONFIGS: try (CarTestService.NativePipe pipe = CarTestService.NativePipe.newPipe()) { mHal.dumpVhal(pipe.getFileDescriptor(), List.of("--restoreVendorConfigs")); writer.print(pipe.getOutput(DEFAULT_HAL_TIMEOUT_MS)); } catch (Exception e) { Slogf.w(TAG, "dumpVhal --genTestVendorConfigs Failed", e); return showInvalidArguments(writer); } break; case COMMAND_SET_PROPERTY_VALUE: setPropertyValue(args, writer); break; case COMMAND_PROJECTION_UI_MODE: if (args.length != 2) { return showInvalidArguments(writer); } mCarProjectionService.setUiMode(Integer.valueOf(args[1])); break; case COMMAND_PROJECTION_AP_TETHERING: if (args.length != 2) { return showInvalidArguments(writer); } mCarProjectionService.setAccessPointTethering(Boolean.parseBoolean(args[1])); break; case COMMAND_PROJECTION_AP_STABLE_CONFIG: if (args.length != 2) { return showInvalidArguments(writer); } mCarProjectionService.setStableLocalOnlyHotspotConfig( Boolean.parseBoolean(args[1])); break; case COMMAND_RESUME: mCarPowerManagementService.forceSimulatedResume(); writer.println("Resume: Simulating resuming from Deep Sleep"); break; case COMMAND_SUSPEND: // fall-through case COMMAND_HIBERNATE: runSuspendCommand(args, writer); break; case COMMAND_SET_DISPLAY_STATE: if (args.length != 3) { return showInvalidArguments(writer); } try { mCarPowerManagementService.setDisplayPowerState( Integer.valueOf(args[1]), Boolean.parseBoolean(args[2])); } catch (Exception e) { Slogf.w(TAG, "Invalid argument: %s %s %s", COMMAND_SET_DISPLAY_STATE, args[1], args[2]); return showInvalidArguments(writer); } break; case COMMAND_SET_UID_TO_ZONE: if (args.length != 3) { return showInvalidArguments(writer); } runSetZoneIdForUid(args[1], args[2]); break; case COMMAND_RESET_VOLUME_CONTEXT: if (args.length > 1) { return showInvalidArguments(writer); } runResetSelectedVolumeContext(); break; case COMMAND_SET_MUTE_CAR_VOLUME_GROUP: if (args.length != 4) { return showInvalidArguments(writer); } runSetMuteCarVolumeGroup(args[1], args[2], args[3]); break; case COMMAND_SET_GROUP_VOLUME: if (args.length != 4) { return showInvalidArguments(writer); } runSetGroupVolume(args[1], args[2], args[3]); break; case COMMAND_SET_AUDIO_MIRROR: if (args.length != 3) { return showInvalidArguments(writer); } runSetAudioMirror(args[1], args[2], writer); break; case COMMAND_UNSET_AUDIO_MIRROR: if (args.length < 1) { return showInvalidArguments(writer); } runUnsetAudioMirror(args, writer); break; case COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE: if (args.length != 3) { return showInvalidArguments(writer); } runSetOccupantZoneIdForUserId(args[1], args[2]); break; case COMMAND_SILENT_MODE: { String value = args.length < 2 ? "" : args.length == 2 ? args[1] : "too many arguments"; runSilentCommand(value, writer); break; } case COMMAND_RESET_USER_ID_IN_OCCUPANT_ZONE: if (args.length != 2) { return showInvalidArguments(writer); } runResetOccupantZoneId(args[1]); break; case COMMAND_START_FIXED_ACTIVITY_MODE: startFixedActivity(args, writer); break; case COMMAND_STOP_FIXED_ACTIVITY_MODE: stopFixedMode(args, writer); break; case COMMAND_ENABLE_FEATURE: if (args.length != 2) { return showInvalidArguments(writer); } enableDisableFeature(args, writer, /* enable= */ true); break; case COMMAND_DISABLE_FEATURE: if (args.length != 2) { return showInvalidArguments(writer); } enableDisableFeature(args, writer, /* enable= */ false); break; case COMMAND_INJECT_KEY: if (args.length < 2) { return showInvalidArguments(writer); } injectKey(args, writer); break; case COMMAND_INJECT_MOTION: if (args.length < 2) { return showInvalidArguments(writer); } injectMotion(args, writer); break; case COMMAND_INJECT_ROTARY: if (args.length < 1) { return showInvalidArguments(writer); } injectRotary(args, writer); break; case COMMAND_INJECT_CUSTOM_INPUT: if (args.length < 2) { return showInvalidArguments(writer); } injectCustomInputEvent(args, writer); break; case COMMAND_GET_INITIAL_USER_INFO: getInitialUserInfo(args, writer); break; case COMMAND_SWITCH_USER: switchUser(args, writer); break; case COMMAND_LOGOUT_USER: logoutUser(args, writer); break; case COMMAND_REMOVE_USER: removeUser(args, writer); break; case COMMAND_CREATE_USER: createUser(args, writer); break; case COMMAND_GET_INITIAL_USER: getInitialUser(writer); break; case COMMAND_GET_USER_AUTH_ASSOCIATION: getUserAuthAssociation(args, writer); break; case COMMAND_SET_USER_AUTH_ASSOCIATION: setUserAuthAssociation(args, writer); break; case COMMAND_SET_START_BG_USERS_ON_GARAGE_MODE: setStartBackgroundUsersOnGarageMode(args, writer); break; case COMMAND_EMULATE_DRIVING_STATE: emulateDrivingState(args, writer); break; case COMMAND_DEFINE_POWER_POLICY: return definePowerPolicy(args, writer); case COMMAND_APPLY_POWER_POLICY: return applyPowerPolicy(args, writer); case COMMAND_DEFINE_POWER_POLICY_GROUP: return definePowerPolicyGroup(args, writer); case COMMAND_SET_POWER_POLICY_GROUP: return setPowerPolicyGroup(args, writer); case COMMAND_APPLY_CTS_VERIFIER_POWER_OFF_POLICY: return applyCtsVerifierPowerOffPolicy(args, writer); case COMMAND_APPLY_CTS_VERIFIER_POWER_ON_POLICY: return applyCtsVerifierPowerOnPolicy(args, writer); case COMMAND_POWER_OFF: powerOff(args, writer); break; case COMMAND_SET_REARVIEW_CAMERA_ID: setRearviewCameraId(args, writer); break; case COMMAND_GET_REARVIEW_CAMERA_ID: getRearviewCameraId(writer); break; case COMMAND_SET_CAMERA_ID: setCameraId(args, writer); break; case COMMAND_GET_CAMERA_ID: getCameraId(args, writer); break; case COMMAND_ENABLE_CAMERA_SERVICE_TYPE: enableCameraServiceType(args, writer); break; case COMMAND_CHECK_CAMERA_SERVICE_TYPE_ENABLED: checkCameraServiceTypeEnabled(args, writer); break; case COMMAND_WATCHDOG_CONTROL_PACKAGE_KILLABLE_STATE: controlWatchdogPackageKillableState(args, writer); break; case COMMAND_WATCHDOG_IO_SET_3P_FOREGROUND_BYTES: setWatchdogIoThirdPartyForegroundBytes(args, writer); break; case COMMAND_WATCHDOG_IO_GET_3P_FOREGROUND_BYTES: getWatchdogIoThirdPartyForegroundBytes(writer); break; case COMMAND_WATCHDOG_CONTROL_PROCESS_HEALTH_CHECK: controlWatchdogProcessHealthCheck(args, writer); break; case COMMAND_WATCHDOG_RESOURCE_OVERUSE_KILL: performResourceOveruseKill(args, writer); break; case COMMAND_DRIVING_SAFETY_SET_REGION: setDrivingSafetyRegion(args, writer); break; case COMMAND_TELEMETRY: handleTelemetryCommands(args, writer); break; case COMMAND_CONTROL_COMPONENT_ENABLED_STATE: controlComponentEnabledState(args, writer); break; case COMMAND_CHECK_LOCK_IS_SECURE: checkLockIsSecure(args, writer); break; case COMMAND_CHECK_FAKE_VHAL: writer.printf("Car Service connects to FakeVehicleStub: %b\n", mHal.isFakeModeEnabled()); break; case COMMAND_LIST_VHAL_PROPS: listVhalProps(writer); break; case COMMAND_GET_VHAL_BACKEND: getVhalBackend(writer); break; case COMMAND_TEST_ECHO_REVERSE_BYTES: testEchoReverseBytes(args, writer); break; case COMMAND_GET_TARGET_CAR_VERSION: getTargetCarVersion(args, writer); break; case COMMAND_SET_PROCESS_GROUP: setProcessGroup(args, writer); break; case COMMAND_GET_PROCESS_GROUP: getProcessGroup(args, writer); break; case COMMAND_SET_PROCESS_PROFILE: setProcessProfile(args, writer); break; case COMMAND_GET_DISPLAY_BY_USER: getDisplayByUser(args, writer); break; case COMMAND_GET_USER_BY_DISPLAY: getUserByDisplay(args, writer); break; case COMMAND_GET_CURRENT_UX_RESTRICTIONS: getCurrentUxRestrictions(args, writer); break; case COMMAND_SET_CURRENT_UXR_MODE: setCurrentUxrMode(args, writer); break; case COMMAND_GET_CURRENT_UXR_MODE: getCurrentUxrMode(args, writer); break; case COMMAND_GET_SUPPORTED_UXR_MODES: getSupportedUxRModes(writer); break; case COMMAND_GET_UXR_CONFIG: getUxrConfig(writer); break; case COMMAND_GET_INPUT_AND_DISPLAY_INFO: getInputAndDisplayInfo(writer); break; case COMMAND_ADD_INPUT_DESCRIPTOR_ASSOCIATION_TO_DISPLAY_UNIQUE_ID: addInputDescriptorAssociationToDisplayUniqueId(args, writer); break; case COMMAND_REMOVE_INPUT_DESCRIPTOR_ASSOCIATION: removeInputDescriptorAssociation(args, writer); break; default: writer.println("Unknown command: \"" + cmd + "\""); showHelp(writer); return RESULT_ERROR; } return RESULT_OK; } private void getUxrConfig(IndentingPrintWriter writer) { writer.println(mCarUxRestrictionsManagerService.getConfigs()); } private void getSupportedUxRModes(IndentingPrintWriter writer) { writer.println(mCarUxRestrictionsManagerService.getSupportedRestrictionModes()); } private void getCurrentUxrMode(String[] args, IndentingPrintWriter writer) { writer.printf("Current Uxr restrictions mode: %s\n", mCarUxRestrictionsManagerService.getRestrictionMode()); } private void setCurrentUxrMode(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { writer.println("Insufficient number of args"); return; } String mode = args[1]; mCarUxRestrictionsManagerService.setRestrictionMode(mode); writer.printf("Current Uxr restrictions mode set to: %s\n", mode); } private void getCurrentUxRestrictions(String[] args, IndentingPrintWriter writer) { int displayId = Display.DEFAULT_DISPLAY; if (args.length == 2) { displayId = Integer.parseInt(args[1]); } CarUxRestrictions restrictions = mCarUxRestrictionsManagerService .getCurrentUxRestrictions(displayId); writer.printf("Current Restrictions:\n %s", restrictions.getActiveRestrictionsString()); } private void getInputAndDisplayInfo(IndentingPrintWriter writer) { InputManager inputManager = mContext.getSystemService(InputManager.class); int[] inputDeviceIds = inputManager.getInputDeviceIds(); for (int inputDeviceId : inputDeviceIds) { InputDevice device = inputManager.getInputDevice(inputDeviceId); String deviceInfo = "Input Device " + device.getId() + ": " + device.getName() + "\n" + "\tDescriptor: " + device.getDescriptor() + "\n"; writer.printf(deviceInfo); } DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); Display[] displays = displayManager.getDisplays(); for (Display display : displays) { String displayInfo = "Display Devices " + display.getDisplayId() + ": \n" + "\tUniqueId: " + DisplayManagerHelper.getUniqueId(display) + " \n"; writer.printf(displayInfo); } } private void addInputDescriptorAssociationToDisplayUniqueId(String[] args, IndentingPrintWriter writer) { InputManager inputManager = mContext.getSystemService(InputManager.class); if (args.length == 3) { InputManagerHelper.addUniqueIdAssociationByDescriptor(inputManager, args[1], args[2]); writer.println("Associated input " + args[1] + " with display " + args[2] + "\n"); } else { writer.printf("Incorrect Usage.\nUsage: %s \n", COMMAND_ADD_INPUT_DESCRIPTOR_ASSOCIATION_TO_DISPLAY_UNIQUE_ID); } } private void removeInputDescriptorAssociation(String[] args, IndentingPrintWriter writer) { InputManager inputManager = mContext.getSystemService(InputManager.class); if (args.length == 2) { InputManagerHelper.removeUniqueIdAssociationByDescriptor(inputManager, args[1]); writer.println("Input " + args[1] + " association removed.\n"); } else { writer.printf("Incorrect Usage.\nUsage: %s \n", COMMAND_REMOVE_INPUT_DESCRIPTOR_ASSOCIATION); } } private void setStartBackgroundUsersOnGarageMode(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { writer.println("Insufficient number of args"); return; } boolean enabled = Boolean.parseBoolean(args[1]); Slogf.d(TAG, "setStartBackgroundUsersOnGarageMode(): " + (enabled ? "enabled" : "disabled")); mCarUserService.setStartBackgroundUsersOnGarageMode(enabled); writer.printf("StartBackgroundUsersOnGarageMode set to %b\n", enabled); } private void startFixedActivity(String[] args, IndentingPrintWriter writer) { if (args.length != 4) { writer.println("Incorrect number of arguments"); showHelp(writer); return; } int displayId; try { displayId = Integer.parseInt(args[1]); } catch (NumberFormatException e) { writer.println("Wrong display id:" + args[1]); return; } String packageName = args[2]; String activityName = args[3]; Intent intent = new Intent(); intent.setComponent(new ComponentName(packageName, activityName)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchDisplayId(displayId); if (!mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent, options, displayId, ActivityManager.getCurrentUser())) { writer.println("Failed to start"); return; } writer.println("Succeeded"); } private void stopFixedMode(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { writer.println("Incorrect number of arguments"); showHelp(writer); return; } int displayId; try { displayId = Integer.parseInt(args[1]); } catch (NumberFormatException e) { writer.println("Wrong display id:" + args[1]); return; } mFixedActivityService.stopFixedActivityMode(displayId); } private void enableDisableFeature(String[] args, IndentingPrintWriter writer, boolean enable) { if (Binder.getCallingUid() != Process.ROOT_UID) { writer.println("Only allowed to root/su"); return; } String featureName = args[1]; long id = Binder.clearCallingIdentity(); // no permission check here int r; if (enable) { r = mFeatureController.enableFeature(featureName); } else { r = mFeatureController.disableFeature(featureName); } switch (r) { case Car.FEATURE_REQUEST_SUCCESS: if (enable) { writer.println("Enabled feature:" + featureName); } else { writer.println("Disabled feature:" + featureName); } break; case Car.FEATURE_REQUEST_ALREADY_IN_THE_STATE: if (enable) { writer.println("Already enabled:" + featureName); } else { writer.println("Already disabled:" + featureName); } break; case Car.FEATURE_REQUEST_MANDATORY: writer.println("Cannot change mandatory feature:" + featureName); break; case Car.FEATURE_REQUEST_NOT_EXISTING: writer.println("Non-existing feature:" + featureName); break; default: writer.println("Unknown error:" + r); break; } Binder.restoreCallingIdentity(id); } private static int decodeAreaId(String areaIdString) { try { return Integer.decode(areaIdString); } catch (NumberFormatException e) { throw new NumberFormatException( "Failed to decode area ID from: " + areaIdString + " - exception: " + e); } } private static int decodePropertyId(String propertyIdString) { if (toPropertyId(propertyIdString) != null) { return toPropertyId(propertyIdString).intValue(); } try { return Integer.decode(propertyIdString); } catch (NumberFormatException e) { throw new NumberFormatException( "Failed to decode property ID from: " + propertyIdString + " - exception: " + e); } } private void injectKey(String[] args, IndentingPrintWriter writer) { int i = 1; // 0 is command itself int display = CarOccupantZoneManager.DISPLAY_TYPE_MAIN; int delayMs = 0; int keyCode = KeyEvent.KEYCODE_UNKNOWN; int action = -1; int seat = SEAT_UNKNOWN; try { while (i < args.length) { switch (args[i]) { case "-d": i++; int vehicleDisplay = Integer.parseInt(args[i]); if (!checkVehicleDisplay(vehicleDisplay, writer)) { return; } display = InputHalService.convertDisplayType(vehicleDisplay); break; case "-t": i++; delayMs = Integer.parseInt(args[i]); break; case "-a": i++; if (args[i].equalsIgnoreCase("down")) { action = KeyEvent.ACTION_DOWN; } else if (args[i].equalsIgnoreCase("up")) { action = KeyEvent.ACTION_UP; } else { throw new IllegalArgumentException("Invalid action: " + args[i]); } break; case "-s": i++; seat = parseUnsignedInt(args[i]); break; default: if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { throw new IllegalArgumentException("key_code already set:" + keyCode); } keyCode = Integer.parseInt(args[i]); } i++; } } catch (NumberFormatException e) { writer.println("Invalid args:" + e); showHelp(writer); return; } if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { writer.println("Missing key code or invalid keycode"); showHelp(writer); return; } if (delayMs < 0) { writer.println("Invalid delay:" + delayMs); showHelp(writer); return; } if (action == -1) { injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, display, seat); SystemClock.sleep(delayMs); injectKeyEvent(KeyEvent.ACTION_UP, keyCode, display, seat); } else { injectKeyEvent(action, keyCode, display, seat); } writer.println("Succeeded"); } private void injectKeyEvent(int action, int keyCode, int display, int seat) { long currentTime = SystemClock.uptimeMillis(); if (action == KeyEvent.ACTION_DOWN) mKeyDownTime = currentTime; long token = Binder.clearCallingIdentity(); try { KeyEvent keyEvent = new KeyEvent(/* downTime= */ mKeyDownTime, /* eventTime= */ currentTime, action, keyCode, /* repeat= */ 0); if (seat == SEAT_UNKNOWN) { // Overwrite with the driver seat seat = mCarOccupantZoneService.getDriverSeat(); } mCarInputService.injectKeyEventForSeat(keyEvent, display, seat); } finally { Binder.restoreCallingIdentity(token); } } private void injectMotion(String[] args, IndentingPrintWriter writer) { int i = 1; // 0 is command itself int display = CarOccupantZoneManager.DISPLAY_TYPE_MAIN; int delayMs = 0; int action = -1; int seat = SEAT_UNKNOWN; int count = 1; List coordXs = new ArrayList<>(); List coordYs = new ArrayList<>(); List pointerIds = new ArrayList<>(); int source = InputDevice.SOURCE_TOUCHSCREEN; try { while (i < args.length) { switch (args[i]) { case "-d": i++; int vehicleDisplay = Integer.parseInt(args[i]); if (!checkVehicleDisplay(vehicleDisplay, writer)) { return; } display = InputHalService.convertDisplayType(vehicleDisplay); break; case "-t": i++; delayMs = Integer.parseInt(args[i]); break; case "-a": i++; if (args[i].equalsIgnoreCase("down")) { action = MotionEvent.ACTION_DOWN; } else if (args[i].equalsIgnoreCase("up")) { action = MotionEvent.ACTION_UP; } else if (args[i].equalsIgnoreCase("move")) { action = MotionEvent.ACTION_MOVE; } else if (args[i].equalsIgnoreCase("cancel")) { action = MotionEvent.ACTION_CANCEL; } else { action = parseUnsignedInt(args[i]); } break; case "-s": i++; seat = parseUnsignedInt(args[i]); break; case "-c": i++; count = Integer.parseInt(args[i]); break; case "-p": for (int j = 0; j < count; j++) { i++; pointerIds.add(Integer.parseInt(args[i])); } break; case "--source": i++; if (MOTION_EVENT_SOURCES.containsKey(args[i])) { source = MOTION_EVENT_SOURCES.get(args[i]); } break; default: int remainedArgNum = args.length - i; if (remainedArgNum != (count * 2)) { throw new IllegalArgumentException("Invalid coordinate: It needs " + count + " coordinates."); } while (i < args.length) { coordXs.add(Float.parseFloat(args[i])); i++; coordYs.add(Float.parseFloat(args[i])); i++; } } i++; } } catch (NumberFormatException e) { writer.println("Invalid args:" + e); showHelp(writer); return; } if (delayMs < 0) { writer.println("Invalid delay:" + delayMs); showHelp(writer); return; } if (coordXs.isEmpty() || coordYs.isEmpty()) { writer.println("Missing x, y coordinate"); showHelp(writer); return; } if (action == -1) { injectMotionEvent(source, MotionEvent.ACTION_DOWN, coordXs, coordYs, pointerIds, display, seat); SystemClock.sleep(delayMs); injectMotionEvent(source, MotionEvent.ACTION_UP, coordXs, coordYs, pointerIds, display, seat); } else { injectMotionEvent(source, action, coordXs, coordYs, pointerIds, display, seat); } writer.println("Succeeded"); } // value can be a hex or decimal string. private static int parseUnsignedInt(String value) { if (value.startsWith("0x")) { return Integer.parseUnsignedInt(value.substring(2), 16); } else { return Integer.parseUnsignedInt(value); } } private static int getToolType(int inputSource) { switch(inputSource) { case InputDevice.SOURCE_MOUSE: case InputDevice.SOURCE_MOUSE_RELATIVE: case InputDevice.SOURCE_TRACKBALL: return MotionEvent.TOOL_TYPE_MOUSE; case InputDevice.SOURCE_STYLUS: case InputDevice.SOURCE_BLUETOOTH_STYLUS: return MotionEvent.TOOL_TYPE_STYLUS; case InputDevice.SOURCE_TOUCHPAD: case InputDevice.SOURCE_TOUCHSCREEN: case InputDevice.SOURCE_TOUCH_NAVIGATION: return MotionEvent.TOOL_TYPE_FINGER; } return MotionEvent.TOOL_TYPE_UNKNOWN; } private static int getInputDeviceId(int inputSource) { int[] devIds = InputDevice.getDeviceIds(); for (int devId : devIds) { InputDevice inputDev = InputDevice.getDevice(devId); if (inputDev.supportsSource(inputSource)) { return devId; } } return DEFAULT_DEVICE_ID; } private void injectMotionEvent(int source, int action, List xs, List ys, List pointerIds, int display, int seat) { long currentTime = SystemClock.uptimeMillis(); if (action == MotionEvent.ACTION_DOWN) { mMotionDownTime = currentTime; } long token = Binder.clearCallingIdentity(); int pointerCount = xs.size(); float pressure = NO_PRESSURE; if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) { pressure = DEFAULT_PRESSURE; } MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[pointerCount]; MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount]; int toolType = getToolType(source); for (int i = 0; i < pointerCount; i++) { pointerProperties[i] = new MotionEvent.PointerProperties(); pointerProperties[i].id = pointerIds.isEmpty() ? i : pointerIds.get(i); pointerProperties[i].toolType = toolType; pointerCoords[i] = new MotionEvent.PointerCoords(); pointerCoords[i].x = xs.get(i); pointerCoords[i].y = ys.get(i); pointerCoords[i].pressure = pressure; pointerCoords[i].size = DEFAULT_SIZE; } MotionEvent event = MotionEvent.obtain( /* downTime= */ mMotionDownTime, /* when= */ currentTime, action, pointerCount, pointerProperties, pointerCoords, DEFAULT_META_STATE, DEFAULT_BUTTON_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, getInputDeviceId(source), DEFAULT_EDGE_FLAGS, source, DEFAULT_FLAGS ); try { if (seat == SEAT_UNKNOWN) { // Overwrite with the driver seat seat = mCarOccupantZoneService.getDriverSeat(); } mCarInputService.injectMotionEventForSeat(event, display, seat); } finally { Binder.restoreCallingIdentity(token); } } private void injectRotary(String[] args, IndentingPrintWriter writer) { int i = 1; // 0 is command itself int display = CarOccupantZoneManager.DISPLAY_TYPE_MAIN; int inputType = CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION; boolean clockwise = false; List deltaTimeMs = new ArrayList<>(); try { while (i < args.length) { switch (args[i]) { case "-d": i++; int vehicleDisplay = Integer.parseInt(args[i]); if (!checkVehicleDisplay(vehicleDisplay, writer)) { return; } display = InputHalService.convertDisplayType(vehicleDisplay); break; case "-i": i++; inputType = Integer.parseInt(args[i]); break; case "-c": i++; clockwise = Boolean.parseBoolean(args[i]); break; case "-dt": i++; while (i < args.length) { deltaTimeMs.add(Long.parseLong(args[i])); i++; } break; default: writer.println("Invalid option at index " + i + ": " + args[i]); return; } i++; } } catch (NumberFormatException e) { writer.println("Invalid args:" + e); showHelp(writer); return; } if (deltaTimeMs.isEmpty()) { deltaTimeMs.add(0L); } for (int j = 0; j < deltaTimeMs.size(); j++) { if (deltaTimeMs.get(j) < 0) { writer.println("Delta time shouldn't be negative: " + deltaTimeMs.get(j)); showHelp(writer); return; } if (j > 0 && deltaTimeMs.get(j) > deltaTimeMs.get(j - 1)) { writer.println("Delta times should be in descending order"); showHelp(writer); return; } } long[] uptimeMs = new long[deltaTimeMs.size()]; long currentUptime = SystemClock.uptimeMillis(); for (int j = 0; j < deltaTimeMs.size(); j++) { uptimeMs[j] = currentUptime - deltaTimeMs.get(j); } RotaryEvent rotaryEvent = new RotaryEvent(inputType, clockwise, uptimeMs); mCarInputService.onRotaryEvent(rotaryEvent, display); writer.println("Succeeded in injecting: " + rotaryEvent); } private void injectCustomInputEvent(String[] args, IndentingPrintWriter writer) { int display = CarOccupantZoneManager.DISPLAY_TYPE_MAIN; int repeatCounter = 1; int argIdx = 1; for (; argIdx < args.length - 1; argIdx++) { switch (args[argIdx]) { case "-d": int vehicleDisplay = Integer.parseInt(args[++argIdx]); if (!checkVehicleDisplay(vehicleDisplay, writer)) { return; } display = InputHalService.convertDisplayType(vehicleDisplay); break; case "-r": repeatCounter = Integer.parseInt(args[++argIdx]); break; default: writer.printf("Unrecognized argument: {%s}\n", args[argIdx]); writer.println("Pass -help to see the full list of options"); return; } } if (argIdx == args.length) { writer.println("Last mandatory argument (fn) not passed."); writer.println("Pass -help to see the full list of options"); return; } // Processing the last remaining argument. Argument is expected one of the tem functions // ('f1', 'f2', ..., 'f10') or just a plain integer representing the custom input event. String eventValue = args[argIdx].toLowerCase(Locale.US); Integer inputCode; if (eventValue.startsWith("f")) { inputCode = CUSTOM_INPUT_FUNCTION_ARGS.get(eventValue); if (inputCode == null) { writer.printf("Invalid input event value {%s}, valid values are f1, f2, ..., f10\n", eventValue); writer.println("Pass -help to see the full list of options"); return; } } else { inputCode = Integer.parseInt(eventValue); } CustomInputEvent event = new CustomInputEvent(inputCode, display, repeatCounter); mCarInputService.onCustomInputEvent(event); writer.printf("Succeeded in injecting {%s}\n", event); } private boolean checkVehicleDisplay(int vehicleDisplay, IndentingPrintWriter writer) { if (vehicleDisplay != VehicleDisplay.MAIN && vehicleDisplay != VehicleDisplay.INSTRUMENT_CLUSTER) { writer.println("Invalid display:" + vehicleDisplay); showHelp(writer); return false; } return true; } private void getInitialUserInfo(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { writer.println("Insufficient number of args"); return; } // Gets the request type String typeArg = args[1]; int requestType = UserHalHelper.parseInitialUserInfoRequestType(typeArg); int timeout = DEFAULT_HAL_TIMEOUT_MS; for (int i = 2; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--timeout": timeout = Integer.parseInt(args[++i]); break; default: writer.println("Invalid option at index " + i + ": " + arg); return; } } Slogf.d(TAG, "handleGetInitialUserInfo(): type=" + requestType + " (" + typeArg + "), timeout=" + timeout); CountDownLatch latch = new CountDownLatch(1); HalCallback callback = (status, resp) -> { try { Slogf.d(TAG, "GetUserInfoResponse: status=" + status + ", resp=" + resp); writer.printf("Call status: %s\n", UserHalHelper.halCallbackStatusToString(status)); if (status != HalCallback.STATUS_OK) { return; } writer.printf("Request id: %d\n", resp.requestId); writer.printf("Action: %s\n", DebugUtils.constantToString( InitialUserInfoResponseAction.class, resp.action)); if (!TextUtils.isEmpty(resp.userNameToCreate)) { writer.printf("User name: %s\n", resp.userNameToCreate); } if (resp.userToSwitchOrCreate.userId != UserManagerHelper.USER_NULL) { writer.printf("User id: %d\n", resp.userToSwitchOrCreate.userId); } if (resp.userToSwitchOrCreate.flags != 0) { writer.printf("User flags: %s\n", UserHalHelper.userFlagsToString(resp.userToSwitchOrCreate.flags)); } if (!TextUtils.isEmpty(resp.userLocales)) { writer.printf("User locales: %s\n", resp.userLocales); } } finally { latch.countDown(); } }; UsersInfo usersInfo = generateUsersInfo(); mHal.getUserHal().getInitialUserInfo(requestType, timeout, usersInfo, callback); waitForHal(writer, latch, timeout); } private UsersInfo generateUsersInfo() { UserManager um = mContext.getSystemService(UserManager.class); UserHandleHelper userHandleHelper = new UserHandleHelper(mContext, um); return UserHalHelper.newUsersInfo(um, userHandleHelper); } private int getUserHalFlags(@UserIdInt int userId) { UserManager um = mContext.getSystemService(UserManager.class); UserHandleHelper userHandleHelper = new UserHandleHelper(mContext, um); return UserHalHelper.getFlags(userHandleHelper, userId); } private static void waitForHal(IndentingPrintWriter writer, CountDownLatch latch, int timeoutMs) { try { if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) { writer.printf("HAL didn't respond in %dms\n", timeoutMs); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); writer.println("Interrupted waiting for HAL"); } } private void switchUser(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { writer.println("Insufficient number of args"); return; } int targetUserId = Integer.parseInt(args[1]); int timeout = DEFAULT_HAL_TIMEOUT_MS + DEFAULT_CAR_USER_SERVICE_TIMEOUT_MS; boolean halOnly = false; boolean ignoreUxr = false; for (int i = 2; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--timeout": timeout = Integer.parseInt(args[++i]); break; case "--hal-only": halOnly = true; break; case "--ignore-uxr": ignoreUxr = true; break; default: writer.println("Invalid option at index " + i + ": " + arg); return; } } Slogf.d(TAG, "switchUser(): target=" + targetUserId + ", halOnly=" + halOnly + ", timeout=" + timeout); if (halOnly) { CountDownLatch latch = new CountDownLatch(1); UserHalService userHal = mHal.getUserHal(); UserInfo targetUserInfo = new UserInfo(); targetUserInfo.userId = targetUserId; targetUserInfo.flags = getUserHalFlags(targetUserId); SwitchUserRequest request = UserHalHelper.emptySwitchUserRequest(); request.targetUser = targetUserInfo; request.usersInfo = generateUsersInfo(); userHal.switchUser(request, timeout, (status, resp) -> { try { Slogf.d(TAG, "SwitchUserResponse: status=" + status + ", resp=" + resp); writer.printf("Call Status: %s\n", UserHalHelper.halCallbackStatusToString(status)); if (status != HalCallback.STATUS_OK) { return; } writer.printf("Request id: %d\n", resp.requestId); writer.printf("Message type: %s\n", DebugUtils.constantToString( SwitchUserMessageType.class, resp.messageType)); writer.printf("Switch Status: %s\n", DebugUtils.constantToString( SwitchUserStatus.class, resp.status)); String errorMessage = resp.errorMessage; if (!TextUtils.isEmpty(errorMessage)) { writer.printf("Error message: %s", errorMessage); } // If HAL returned OK, make a "post-switch" call to the HAL indicating an // Android error. This is to "rollback" the HAL switch. if (status == HalCallback.STATUS_OK && resp.status == SwitchUserStatus.SUCCESS) { userHal.postSwitchResponse(request); } } finally { latch.countDown(); } }); waitForHal(writer, latch, timeout); return; } CarUserManager carUserManager = getCarUserManager(mContext); SyncResultCallback syncResultCallback = new SyncResultCallback<>(); if (ignoreUxr) { carUserManager.switchUserIgnoringUxRestriction( new UserSwitchRequest.Builder(UserHandle.of(targetUserId)).build(), Runnable::run, syncResultCallback); } else { carUserManager.switchUser( new UserSwitchRequest.Builder(UserHandle.of(targetUserId)).build(), Runnable::run, syncResultCallback); } try { showUserSwitchResult(writer, syncResultCallback.get(timeout, TimeUnit.MILLISECONDS)); } catch (TimeoutException e) { writer.printf("UserSwitchResult: timed out waitng for result"); } catch (InterruptedException e) { writer.printf("UserSwitchResult: interrupted waitng for result"); Thread.currentThread().interrupt(); } } private void showUserSwitchResult(IndentingPrintWriter writer, UserSwitchResult result) { if (result == null) return; writer.printf("UserSwitchResult: status=%s", UserSwitchResult.statusToString(result.getStatus())); String msg = result.getErrorMessage(); if (!TextUtils.isEmpty(msg)) { writer.printf(", errorMessage=%s", msg); } writer.println(); } private void logoutUser(String[] args, IndentingPrintWriter writer) { int timeout = DEFAULT_HAL_TIMEOUT_MS + DEFAULT_CAR_USER_SERVICE_TIMEOUT_MS; if (args.length > 1) { for (int i = 1; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--timeout": timeout = Integer.parseInt(args[++i]); break; default: writer.println("Invalid option at index " + i + ": " + arg); return; } } } Slogf.d(TAG, "logoutUser(): timeout=%d", timeout); CarUserManager carUserManager = getCarUserManager(mContext); AsyncFuture future = carUserManager.logoutUser(); UserSwitchResult result = waitForFuture(writer, future, timeout); showUserSwitchResult(writer, result); } private void createUser(String[] args, IndentingPrintWriter writer) { int timeout = DEFAULT_HAL_TIMEOUT_MS + DEFAULT_CAR_USER_SERVICE_TIMEOUT_MS; int flags = 0; boolean isGuest = false; boolean halOnly = false; String name = null; for (int i = 1; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--timeout": timeout = Integer.parseInt(args[++i]); break; case "--guest": isGuest = true; break; case "--hal-only": halOnly = true; break; case "--flags": flags = Integer.parseInt(args[++i]); break; case "--type": // print an error and quit. writer.printf("Error: --type is not supported. Use --guest to create a guest."); writer.println(); return; default: if (name != null) { writer.println("Invalid option at index " + i + ": " + arg); return; } name = arg; } } Slogf.d(TAG, "createUser(): name=" + name + ", flags=" + flags + ", guest=" + isGuest + ", halOnly=" + halOnly + ", timeout=" + timeout); if (!halOnly) { CarUserManager carUserManager = getCarUserManager(mContext); SyncResultCallback syncResultCallback = new SyncResultCallback(); UserCreationRequest.Builder builder = new UserCreationRequest.Builder(); if (name != null) { builder.setName(name); } if (isGuest) { builder.setGuest(); } if ((flags & UserManagerHelper.FLAG_ADMIN) == UserManagerHelper.FLAG_ADMIN) { builder.setAdmin(); } if ((flags & UserManagerHelper.FLAG_EPHEMERAL) == UserManagerHelper.FLAG_EPHEMERAL) { builder.setEphemeral(); } carUserManager.createUser(builder.build(), Runnable::run, syncResultCallback); UserCreationResult result; try { result = syncResultCallback.get(timeout, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { writer.printf("UserCreationResult: Got Timeout Exception - %s", e); return; } catch (InterruptedException e) { Thread.currentThread().interrupt(); writer.printf("UserCreationResult: Got InterruptedException - %s", e); return; } if (result == null) return; UserHandle user = result.getUser(); // NOTE: must always show the id=%d, as it's used by CTS tests writer.printf("UserCreationResult: status=%s, user=%s, id=%d", UserCreationResult.statusToString(result.getStatus()), user == null ? "N/A" : user.toString(), user == null ? UserManagerHelper.USER_NULL : user.getIdentifier()); Integer androidFailureStatus = result.getAndroidFailureStatus(); if (androidFailureStatus != null) { writer.printf(", androidStatus=%s", DebugUtils.constantToString(UserManager.class, "USER_OPERATION_", androidFailureStatus)); } String msg = result.getErrorMessage(); if (!TextUtils.isEmpty(msg)) { writer.printf(", errorMessage=%s", msg); } String internalMsg = result.getInternalErrorMessage(); if (!TextUtils.isEmpty(internalMsg)) { writer.printf(", internalErrorMessage=%s", internalMsg); } writer.println(); return; } CountDownLatch latch = new CountDownLatch(1); UserHalService userHal = mHal.getUserHal(); CreateUserRequest request = UserHalHelper.emptyCreateUserRequest(); UserManager um = mContext.getSystemService(UserManager.class); NewUserRequest newUserRequest; try { newUserRequest = getCreateUserRequest(name, isGuest, flags); } catch (Exception e) { Slogf.e(TAG, e, "Error creating new user request. name: %s isGuest: %b and flags: %s", name, isGuest, flags); writer.println("Failed to create user"); return; } NewUserResponse newUserResponse = um.createUser(newUserRequest); if (!newUserResponse.isSuccessful()) { writer.printf("Failed to create user"); return; } UserHandle newUser = newUserResponse.getUser(); writer.printf("New user: %s\n", newUser); Slogf.i(TAG, "Created new user: " + newUser); request.newUserInfo.userId = newUser.getIdentifier(); request.newUserInfo.flags = UserHalHelper.convertFlags(new UserHandleHelper(mContext, um), newUser); request.usersInfo = generateUsersInfo(); AtomicBoolean halOk = new AtomicBoolean(false); try { userHal.createUser(request, timeout, (status, resp) -> { Slogf.d(TAG, "CreateUserResponse: status=" + status + ", resp=" + resp); writer.printf("Call Status: %s\n", UserHalHelper.halCallbackStatusToString(status)); if (status == HalCallback.STATUS_OK) { halOk.set(resp.status == CreateUserStatus.SUCCESS); writer.printf("Request id: %d\n", resp.requestId); writer.printf("Create Status: %s\n", DebugUtils.constantToString( CreateUserStatus.class, resp.status)); String errorMessage = resp.errorMessage; if (!TextUtils.isEmpty(errorMessage)) { writer.printf("Error message: %s", errorMessage); } } latch.countDown(); }); waitForHal(writer, latch, timeout); } catch (RuntimeException e) { writer.printf("HAL failed: %s\n", e); } finally { if (!halOk.get()) { writer.printf("Removing user %d due to HAL failure\n", newUser.getIdentifier()); boolean removed = um.removeUser(newUser); writer.printf("User removed: %b\n", removed); } } } private NewUserRequest getCreateUserRequest(String name, boolean isGuest, int flags) { NewUserRequest.Builder builder = new NewUserRequest.Builder().setName(name); if ((flags & UserManagerHelper.FLAG_ADMIN) == UserManagerHelper.FLAG_ADMIN) { builder.setAdmin(); } if ((flags & UserManagerHelper.FLAG_EPHEMERAL) == UserManagerHelper.FLAG_EPHEMERAL) { builder.setEphemeral(); } if (isGuest) { builder.setUserType(UserManager.USER_TYPE_FULL_GUEST); } return builder.build(); } private void removeUser(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { writer.println("Insufficient number of args"); return; } int userId = Integer.parseInt(args[1]); int timeout = DEFAULT_HAL_TIMEOUT_MS + DEFAULT_CAR_USER_SERVICE_TIMEOUT_MS; boolean halOnly = false; for (int i = 2; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--hal-only": halOnly = true; break; default: writer.println("Invalid option at index " + i + ": " + arg); return; } } Slogf.d(TAG, "handleRemoveUser(): User to remove=" + userId + ", halOnly=" + halOnly); if (halOnly) { UserHalService userHal = mHal.getUserHal(); UsersInfo usersInfo = generateUsersInfo(); UserInfo userInfo = new UserInfo(); userInfo.userId = userId; userInfo.flags = getUserHalFlags(userId); RemoveUserRequest request = UserHalHelper.emptyRemoveUserRequest(); request.removedUserInfo = userInfo; request.usersInfo = usersInfo; userHal.removeUser(request); writer.printf("User removal sent for HAL only.\n"); return; } CarUserManager carUserManager = getCarUserManager(mContext); SyncResultCallback syncResultCallback = new SyncResultCallback<>(); carUserManager.removeUser(new UserRemovalRequest.Builder( UserHandle.of(userId)).build(), Runnable::run, syncResultCallback); try { UserRemovalResult result = syncResultCallback.get(timeout, TimeUnit.MILLISECONDS); writer.printf("UserRemovalResult: status = %s\n", UserRemovalResult.statusToString(result.getStatus())); } catch (TimeoutException e) { writer.printf("UserRemovalResult: timed out waitng for result"); } catch (InterruptedException e) { writer.printf("UserRemovalResult: interrupted waitng for result"); Thread.currentThread().interrupt(); } } private static T waitForFuture(IndentingPrintWriter writer, AsyncFuture future, int timeoutMs) { T result = null; try { result = future.get(timeoutMs, TimeUnit.MILLISECONDS); if (result == null) { writer.printf("Service didn't respond in %d ms", timeoutMs); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException | TimeoutException e) { writer.printf("Exception getting future: %s", e); } return result; } private void getInitialUser(IndentingPrintWriter writer) { UserHandle user = mCarUserService.getInitialUser(); writer.println(user == null ? NO_INITIAL_USER : user.getIdentifier()); } private void getUserAuthAssociation(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { writer.println("invalid usage, must pass at least 1 argument"); return; } boolean halOnly = false; int userId = UserHandle.CURRENT.getIdentifier(); UserIdentificationGetRequest request = UserHalHelper.emptyUserIdentificationGetRequest(); ArrayList associationTypes = new ArrayList<>(); for (int i = 1; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--user": try { userId = Integer.parseInt(args[++i]); } catch (NumberFormatException e) { writer.printf("Invalid user id at index %d (from %s): %s\n", i + 1, Arrays.toString(args), arg); } break; case "--hal-only": halOnly = true; break; default: int type = parseAuthArg(VALID_USER_AUTH_TYPES, arg); if (type == INVALID_USER_AUTH_TYPE_OR_VALUE) { writer.printf("Invalid type at index %d (from %s): %s. %s\n", i + 1, Arrays.toString(args), arg, VALID_USER_AUTH_TYPES_HELP); return; } associationTypes.add(type); } } request.associationTypes = toIntArray(associationTypes); if (userId == UserHandle.CURRENT.getIdentifier()) { userId = ActivityManager.getCurrentUser(); } int requestSize = request.associationTypes.length; if (halOnly) { request.numberAssociationTypes = requestSize; request.userInfo.userId = userId; request.userInfo.flags = getUserHalFlags(userId); Slogf.d(TAG, "getUserAuthAssociation(): user=" + userId + ", halOnly=" + halOnly + ", request=" + request); UserIdentificationResponse response = mHal.getUserHal().getUserAssociation(request); Slogf.d(TAG, "getUserAuthAssociation(): response=" + response); showResponse(writer, response); return; } CarUserManager carUserManager = getCarUserManager(writer, userId); int[] types = new int[requestSize]; for (int i = 0; i < requestSize; i++) { types[i] = request.associationTypes[i]; } UserIdentificationAssociationResponse response = carUserManager .getUserIdentificationAssociation(types); showResponse(writer, response); } private CarUserManager getCarUserManager(IndentingPrintWriter writer, @UserIdInt int userId) { Context context = getContextForUser(userId); int actualUserId = Binder.getCallingUid(); if (actualUserId != userId) { writer.printf("Emulating call for user id %d, but caller's user id is %d, so that's " + "what CarUserService will use when calling HAL.\n", userId, actualUserId); } return getCarUserManager(context); } private Context getContextForUser(int userId) { if (userId == mContext.getUser().getIdentifier()) { return mContext; } return mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0); } private CarUserManager getCarUserManager(Context context) { Car car = Car.createCar(context); CarUserManager carUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE); return carUserManager; } private void showResponse( IndentingPrintWriter writer, UserIdentificationResponse response) { if (response == null) { writer.println("null response"); return; } if (!TextUtils.isEmpty(response.errorMessage)) { writer.printf("Error message: %s\n", response.errorMessage); } int numberAssociations = response.associations.length; writer.printf("%d associations:\n", numberAssociations); for (int i = 0; i < numberAssociations; i++) { UserIdentificationAssociation association = response.associations[i]; writer.printf(" %s\n", association); } } private void showResponse(IndentingPrintWriter writer, UserIdentificationAssociationResponse response) { if (response == null) { writer.println("null response"); return; } if (!response.isSuccess()) { writer.printf("failed response: %s\n", response); return; } String errorMessage = response.getErrorMessage(); if (!TextUtils.isEmpty(errorMessage)) { writer.printf("Error message: %s\n", errorMessage); } int[] values = response.getValues(); if (values == null) { writer.printf("no associations on %s\n", response); return; } writer.printf("%d associations:\n", values.length); for (int i = 0; i < values.length; i++) { writer.printf(" %s\n", DebugUtils.constantToString( UserIdentificationAssociationValue.class, values[i])); } } private void setUserAuthAssociation(String[] args, IndentingPrintWriter writer) { if (args.length < 3) { writer.println("invalid usage, must pass at least 4 arguments"); return; } boolean halOnly = false; int timeout = DEFAULT_HAL_TIMEOUT_MS; int userId = UserHandle.CURRENT.getIdentifier(); UserIdentificationSetRequest request = UserHalHelper.emptyUserIdentificationSetRequest(); ArrayList associations = new ArrayList<>(); for (int i = 1; i < args.length; i++) { String arg = args[i]; switch (arg) { case "--user": try { userId = Integer.parseInt(args[++i]); } catch (NumberFormatException e) { writer.printf("Invalid user id at index %d (from %s): %s\n", i + 1, Arrays.toString(args), arg); } break; case "--hal-only": halOnly = true; break; case "--timeout": timeout = Integer.parseInt(args[++i]); break; default: UserIdentificationSetAssociation association = new UserIdentificationSetAssociation(); association.type = parseAuthArg(VALID_USER_AUTH_TYPES, arg); if (association.type == INVALID_USER_AUTH_TYPE_OR_VALUE) { writer.printf("Invalid type at index %d (from %s): %s. %s\n", i + 1, Arrays.toString(args), arg, VALID_USER_AUTH_TYPES_HELP); return; } association.value = parseAuthArg(VALID_USER_AUTH_SET_VALUES, args[++i]); if (association.value == INVALID_USER_AUTH_TYPE_OR_VALUE) { writer.printf("Invalid value at index %d (from %s): %s. %s\n", i + 1, Arrays.toString(args), arg, VALID_USER_AUTH_SET_VALUES_HELP); return; } associations.add(association); } } int requestSize = associations.size(); request.associations = associations.toArray( new UserIdentificationSetAssociation[requestSize]); if (userId == UserHandle.CURRENT.getIdentifier()) { userId = ActivityManager.getCurrentUser(); } if (halOnly) { request.numberAssociations = requestSize; request.userInfo.userId = userId; request.userInfo.flags = getUserHalFlags(userId); Slogf.d(TAG, "setUserAuthAssociation(): user=" + userId + ", halOnly=" + halOnly + ", request=" + request); CountDownLatch latch = new CountDownLatch(1); mHal.getUserHal().setUserAssociation(timeout, request, (status, response) -> { Slogf.d(TAG, "setUserAuthAssociation(): response=" + response); try { showResponse(writer, response); } finally { latch.countDown(); } }); waitForHal(writer, latch, timeout); return; } CarUserManager carUserManager = getCarUserManager(writer, userId); int[] types = new int[requestSize]; int[] values = new int[requestSize]; for (int i = 0; i < requestSize; i++) { UserIdentificationSetAssociation association = request.associations[i]; types[i] = association.type; values[i] = association.value; } AsyncFuture future = carUserManager .setUserIdentificationAssociation(types, values); UserIdentificationAssociationResponse response = waitForFuture(writer, future, timeout); if (response != null) { showResponse(writer, response); } } private static int parseAuthArg(SparseArray types, String type) { for (int i = 0; i < types.size(); i++) { if (types.valueAt(i).equals(type)) { return types.keyAt(i); } } return INVALID_USER_AUTH_TYPE_OR_VALUE; } private void forceDayNightMode(String arg, IndentingPrintWriter writer) { int mode; switch (arg) { case PARAM_DAY_MODE: mode = CarNightService.FORCED_DAY_MODE; break; case PARAM_NIGHT_MODE: mode = CarNightService.FORCED_NIGHT_MODE; break; case PARAM_SENSOR_MODE: mode = CarNightService.FORCED_SENSOR_MODE; break; default: writer.printf("Unknown value: %s. Valid argument: %s|%s|%s\n", arg, PARAM_DAY_MODE, PARAM_NIGHT_MODE, PARAM_SENSOR_MODE); return; } if (Flags.carNightGlobalSetting()) { Settings.Global.putInt(mContext.getContentResolver(), FORCED_DAY_NIGHT_MODE, mode); writer.println("DayNightMode changed to: " + arg); } else { int current = mCarNightService.forceDayNightMode(mode); String currentMode = null; switch (current) { case UiModeManager.MODE_NIGHT_AUTO: currentMode = PARAM_SENSOR_MODE; break; case UiModeManager.MODE_NIGHT_YES: currentMode = PARAM_NIGHT_MODE; break; case UiModeManager.MODE_NIGHT_NO: currentMode = PARAM_DAY_MODE; break; default: break; } writer.println("DayNightMode changed to: " + currentMode); } } private void runSuspendCommand(String[] args, IndentingPrintWriter writer) { // args[0] is always either COMMAND_SUSPEND or COMMAND_HIBERNE. String command = args[0]; boolean isHibernation = Objects.equals(command, COMMAND_HIBERNATE); // Default is --auto, so simulate is decided based on device capability. boolean simulate = !mCarPowerManagementService.isSuspendAvailable(isHibernation); boolean modeSet = false; boolean skipGarageMode = false; boolean freeMemory = false; int resumeDelay = CarPowerManagementService.NO_WAKEUP_BY_TIMER; int cancelDelay = CarPowerManagementService.NO_WAKEUP_BY_TIMER; int index = 1; while (index < args.length) { switch (args[index]) { case PARAM_SIMULATE: if (modeSet) { writer.printf("Invalid command syntax.\nUsage: %s\n", getSuspendCommandUsage(command)); return; } simulate = true; modeSet = true; break; case PARAM_AUTO: if (modeSet) { writer.printf("Invalid command syntax.\nUsage: %s\n", getSuspendCommandUsage(command)); return; } simulate = !mCarPowerManagementService.isSuspendAvailable(isHibernation); modeSet = true; break; case PARAM_REAL: if (modeSet) { writer.printf("Invalid command syntax.\nUsage: %s\n", getSuspendCommandUsage(command)); return; } simulate = false; modeSet = true; break; case PARAM_SKIP_GARAGEMODE: skipGarageMode = true; break; case PARAM_WAKEUP_AFTER: index++; if (index >= args.length) { writer.printf("Invalid command syntax.\nUsage: %s\n", getSuspendCommandUsage(command)); return; } resumeDelay = Integer.parseInt(args[index]); break; case PARAM_CANCEL_AFTER: index++; if (index >= args.length) { writer.printf("Invalid command syntax.\nUsage: %s\n", getSuspendCommandUsage(command)); return; } cancelDelay = Integer.parseInt(args[index]); break; case PARAM_FREE_MEMORY: freeMemory = true; break; default: writer.printf("Invalid command syntax.\nUsage: %s\n", getSuspendCommandUsage(command)); return; } index++; } if ((cancelDelay >= 0 || resumeDelay >= 0) && !simulate) { writer.printf("Wake up and cancel by timer is available only with simulated suspend." + "\n"); return; } if (cancelDelay >= 0 && resumeDelay >= 0 && simulate) { writer.printf("Cancel and resume cannot be set at the same time.\n"); return; } if (freeMemory && !simulate) { writer.printf("Free memory can be used only with simulated hibernation.\n"); return; } String suspendType = isHibernation ? "disk" : "RAM"; if (simulate) { try { writer.printf("Suspend: simulating suspend-to-%s.\n", suspendType); if (Flags.carPowerCancelShellCommand()) { mCarPowerManagementService.simulateSuspendAndMaybeReboot( isHibernation ? PowerHalService.PowerState.SHUTDOWN_TYPE_HIBERNATION : PowerHalService.PowerState.SHUTDOWN_TYPE_DEEP_SLEEP, /* shouldReboot= */ false, skipGarageMode, resumeDelay, cancelDelay, freeMemory); } else { mCarPowerManagementService.simulateSuspendAndMaybeReboot( isHibernation ? PowerHalService.PowerState.SHUTDOWN_TYPE_HIBERNATION : PowerHalService.PowerState.SHUTDOWN_TYPE_DEEP_SLEEP, /* shouldReboot= */ false, skipGarageMode, resumeDelay, freeMemory); } } catch (Exception e) { writer.printf("Simulating suspend-to-%s failed: %s\n", suspendType, e.getMessage()); } } else { try { mCarPowerManagementService.suspendFromCommand(isHibernation, skipGarageMode); } catch (Exception e) { writer.printf("Suspend to %s failed: %s.\n", suspendType, e.getMessage()); } } } private void forceGarageMode(String arg, IndentingPrintWriter writer) { switch (arg) { case PARAM_ON_MODE: mSystemInterface.setAllDisplayState(false); mGarageModeService.forceStartGarageMode(); writer.println("Garage mode: " + mGarageModeService.isGarageModeActive()); break; case PARAM_OFF_MODE: mSystemInterface.setAllDisplayState(true); mGarageModeService.stopAndResetGarageMode(); writer.println("Garage mode: " + mGarageModeService.isGarageModeActive()); break; case PARAM_QUERY_MODE: mGarageModeService.dump(writer); break; case PARAM_REBOOT_AFTER_GARAGEMODE: writer.printf("\"cmd car_service garagemode reboot\" is deprecated. Use " + "\"cmd car_service power-off --reboot\" next time"); try { mCarPowerManagementService.powerOffFromCommand(/*skipGarageMode= */ false, /* reboot= */ true); writer.println("Entering Garage Mode. Will reboot when it completes."); } catch (IllegalStateException e) { writer.printf("Entering Garage Mode failed: %s\n", e.getMessage()); } break; default: writer.printf("Unknown value: %s. Valid argument: %s|%s|%s|%s\n", arg, PARAM_ON_MODE, PARAM_OFF_MODE, PARAM_QUERY_MODE, PARAM_REBOOT_AFTER_GARAGEMODE); } } private void runSilentCommand(String arg, IndentingPrintWriter writer) { switch (arg) { case SILENT_MODE_FORCED_SILENT: writer.println("Forcing silent mode to silent"); mCarPowerManagementService.setSilentMode(SILENT_MODE_FORCED_SILENT); break; case SILENT_MODE_FORCED_NON_SILENT: writer.println("Forcing silent mode to non-silent"); mCarPowerManagementService.setSilentMode(SILENT_MODE_FORCED_NON_SILENT); break; case SILENT_MODE_NON_FORCED: writer.println("Not forcing silent mode"); mCarPowerManagementService.setSilentMode(SILENT_MODE_NON_FORCED); break; case PARAM_QUERY_MODE: mCarPowerManagementService.dumpSilentMode(writer); break; default: writer.printf("Unknown value: %s. Valid argument: %s|%s|%s|%s\n", arg, SILENT_MODE_FORCED_SILENT, SILENT_MODE_FORCED_NON_SILENT, SILENT_MODE_NON_FORCED, PARAM_QUERY_MODE); } } private void emulateDrivingState(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { writer.println("invalid usage, must pass driving state"); return; } String mode = args[1]; switch (mode) { case DRIVING_STATE_DRIVE: emulateDrive(); break; case DRIVING_STATE_PARK: emulatePark(); break; case DRIVING_STATE_REVERSE: emulateReverse(); break; case DRIVING_STATE_NEUTRAL: emulateNeutral(); break; default: writer.printf("invalid driving mode %s; must be %s or %s\n", mode, DRIVING_STATE_DRIVE, DRIVING_STATE_PARK); } } /** * Emulates driving mode. Called by * {@code adb shell cmd car_service emulate-driving-state drive}. */ private void emulateDrive() { Slogf.i(TAG, "Emulating driving mode (speed=80mph, gear=8)"); mHal.injectVhalEvent(VehiclePropertyIds.PERF_VEHICLE_SPEED, /* areaId= */ 0, /* value= */ "80", /* delayTimeSeconds= */ 2000); mHal.injectVhalEvent(VehiclePropertyIds.GEAR_SELECTION, /* areaId= */ 0, Integer.toString(VehicleGear.GEAR_8), /* delayTimeSeconds= */ 0); mHal.injectVhalEvent(VehiclePropertyIds.PARKING_BRAKE_ON, /* areaId= */ 0, /* value= */ "false", /* delayTimeSeconds= */ 0); } /** * Emulates reverse driving mode. Called by * {@code adb shell cmd car_service emulate-driving-state reverse}. */ private void emulateReverse() { Slogf.i(TAG, "Emulating reverse driving mode (speed=5mph)"); mHal.injectVhalEvent(VehiclePropertyIds.PERF_VEHICLE_SPEED, /* areaId= */ 0, /* value= */ "5", /* delayTimeSeconds= */ 2000); mHal.injectVhalEvent(VehiclePropertyIds.GEAR_SELECTION, /* areaId= */ 0, Integer.toString(VehicleGear.GEAR_REVERSE), /* delayTimeSeconds= */ 0); mHal.injectVhalEvent(VehiclePropertyIds.PARKING_BRAKE_ON, /* areaId= */ 0, /* value= */ "false", /* delayTimeSeconds= */ 0); } /** * Emulates parking mode. Called by * {@code adb shell cmd car_service emulate-driving-state park}. */ private void emulatePark() { Slogf.i(TAG, "Emulating parking mode"); mHal.injectVhalEvent(VehiclePropertyIds.PERF_VEHICLE_SPEED, /* areaId= */ 0, /* value= */ "0", /* delayTimeSeconds= */ 0); mHal.injectVhalEvent(VehiclePropertyIds.GEAR_SELECTION, /* areaId= */ 0, Integer.toString(VehicleGear.GEAR_PARK), /* delayTimeSeconds= */ 0); } /** * Emulates neutral driving state. Called by * {@code adb shell cmd car_service emulate-driving-state neutral}. */ private void emulateNeutral() { Slogf.i(TAG, "Emulating neutral driving mode"); mHal.injectVhalEvent(VehiclePropertyIds.PERF_VEHICLE_SPEED, /* areaId= */ 0, /* value= */ "0", /* delayTimeSeconds= */ 0); mHal.injectVhalEvent(VehiclePropertyIds.GEAR_SELECTION, /* areaId= */ 0, Integer.toString(VehicleGear.GEAR_NEUTRAL), /* delayTimeSeconds= */ 0); mHal.injectVhalEvent(VehiclePropertyIds.PARKING_BRAKE_ON, /* areaId= */ 0, /* value= */ "true", /* delayTimeSeconds= */ 0); } private int definePowerPolicy(String[] args, IndentingPrintWriter writer) { boolean result = mCarPowerManagementService.definePowerPolicyFromCommand(args, writer); if (result) return RESULT_OK; writer.printf("\nUsage: cmd car_service %s [--enable COMP1,COMP2,...] " + "[--disable COMP1,COMP2,...]\n", COMMAND_DEFINE_POWER_POLICY); return RESULT_ERROR; } private int applyPowerPolicy(String[] args, IndentingPrintWriter writer) { boolean result = mCarPowerManagementService.applyPowerPolicyFromCommand(args, writer); if (result) return RESULT_OK; writer.printf("\nUsage: cmd car_service %s \n", COMMAND_APPLY_POWER_POLICY); return RESULT_ERROR; } private int definePowerPolicyGroup(String[] args, IndentingPrintWriter writer) { boolean result = mCarPowerManagementService.definePowerPolicyGroupFromCommand(args, writer); if (result) return RESULT_OK; writer.printf("\nUsage: cmd car_service %s [%s:] " + "[%s:]\n", COMMAND_DEFINE_POWER_POLICY_GROUP, POWER_STATE_WAIT_FOR_VHAL, POWER_STATE_ON); return RESULT_ERROR; } private int setPowerPolicyGroup(String[] args, IndentingPrintWriter writer) { boolean result = mCarPowerManagementService.setPowerPolicyGroupFromCommand(args, writer); if (result) return RESULT_OK; writer.printf("\nUsage: cmd car_service %s \n", COMMAND_SET_POWER_POLICY_GROUP); return RESULT_ERROR; } private int applyCtsVerifierPowerPolicy(String policyId, String ops, String cmdName, IndentingPrintWriter writer) { String[] defArgs = {"define-power-policy", policyId, ops, "WIFI,BLUETOOTH,LOCATION"}; mCarPowerManagementService.definePowerPolicyFromCommand(defArgs, writer); String[] appArgs = {"apply-power-policy", policyId}; boolean result = mCarPowerManagementService.applyPowerPolicyFromCommand(appArgs, writer); if (result) return RESULT_OK; writer.printf("\nUsage: cmd car_service %s\n", cmdName); return RESULT_ERROR; } private int applyCtsVerifierPowerOffPolicy(String[] unusedArgs, IndentingPrintWriter writer) { return applyCtsVerifierPowerPolicy("cts_verifier_off", "--disable", COMMAND_APPLY_CTS_VERIFIER_POWER_OFF_POLICY, writer); } private int applyCtsVerifierPowerOnPolicy(String[] unusedArgs, IndentingPrintWriter writer) { return applyCtsVerifierPowerPolicy("cts_verifier_on", "--enable", COMMAND_APPLY_CTS_VERIFIER_POWER_ON_POLICY, writer); } private void powerOff(String[] args, IndentingPrintWriter writer) { boolean skipGarageMode = false; boolean reboot = false; int index = 1; while (index < args.length) { switch (args[index]) { case PARAM_SKIP_GARAGEMODE: skipGarageMode = true; break; case PARAM_REBOOT: reboot = true; break; default: writer.printf("Invalid usage: %s [%s] [%s]\n", COMMAND_POWER_OFF, PARAM_SKIP_GARAGEMODE, PARAM_REBOOT); return; } index++; } mCarPowerManagementService.powerOffFromCommand(skipGarageMode, reboot); } /** * Get config for VHAL property * * @param args the command line arguments to parse for VHAL property details * @param writer IndentingPrintWriter */ private void getCarPropertyConfig(String[] args, IndentingPrintWriter writer) { String propertyIdString = args.length < 2 ? PARAM_ALL_PROPERTIES_OR_AREA_IDS : args[1]; mHal.dumpPropertyConfigs(writer, decodePropertyId(propertyIdString)); } /** * Get current value for VHAL property * * @param args the command line arguments to parse for VHAL property details * @param writer IndentingPrintWriter */ private void getPropertyValue(String[] args, IndentingPrintWriter writer) { String propertyIdString = args.length < 2 ? PARAM_ALL_PROPERTIES_OR_AREA_IDS : args[1]; String areaIdString = args.length < 3 ? PARAM_ALL_PROPERTIES_OR_AREA_IDS : args[2]; mHal.dumpPropertyValueByCommand(writer, decodePropertyId(propertyIdString), decodeAreaId(areaIdString)); } /** * Inject a fake VHAL event * * @param args the command line arguments to parse for VHAL event details * @param writer IndentingPrintWriter */ private void injectVhalEvent(String[] args, IndentingPrintWriter writer) { int argNum = args.length; if (argNum < 3 || argNum > 6) { showInvalidArguments(writer); return; } int propertyId = decodePropertyId(args[1]); String delayTimeSeconds = Objects.equals(args[argNum - 2], "-t") ? args[argNum - 1] : "0"; int areaId; String value; if (argNum == 4 || argNum == 6) { areaId = decodeAreaId(args[2]); value = args[3]; } else { // area ID is not specified, assume global area ID if (!isPropertyAreaTypeGlobal(propertyId)) { writer.println("Property " + toPropertyIdString(propertyId) + " is not a global area type property. The area ID must be specified. " + "Skipping injection."); return; } areaId = 0; value = args[2]; } String debugOutput = "Injecting VHAL event: property=" + toPropertyIdString(propertyId) + ", areaId=" + toAreaIdString(propertyId, areaId) + ", value=" + value + ( TextUtils.isEmpty(delayTimeSeconds) ? "" : ", delayTimeSeconds=" + delayTimeSeconds); Slogf.i(TAG, debugOutput); writer.println(debugOutput); mHal.injectVhalEvent(propertyId, areaId, value, Integer.decode(delayTimeSeconds)); } /** * Inject a fake VHAL error error event * * @param args the command line arguments to parse for error event details * @param writer IndentingPrintWriter */ private void injectErrorEvent(String[] args, IndentingPrintWriter writer) { if (args.length != 4) { showInvalidArguments(writer); return; } int propertyId = decodePropertyId(args[1]); int areaId = decodeAreaId(args[2]); int errorCode = Integer.decode(args[3]); Slogf.i(TAG, "Injecting VHAL error event: property=" + toPropertyIdString( propertyId) + ", areaId=" + toAreaIdString(propertyId, areaId) + ", errorCode=" + errorCode); VehiclePropError vehiclePropError = new VehiclePropError(); vehiclePropError.propId = propertyId; vehiclePropError.areaId = areaId; vehiclePropError.errorCode = errorCode; mHal.onPropertySetError(new ArrayList(List.of(vehiclePropError))); } // Inject continuous vhal events. private void injectContinuousEvents(String[] args, IndentingPrintWriter writer) { if (args.length < 3 || args.length > 8) { showInvalidArguments(writer); return; } String areaId = PARAM_VEHICLE_PROPERTY_GLOBAL_AREA_ID; String sampleRate = PARAM_INJECT_EVENT_DEFAULT_RATE; String durationTime = PARAM_INJECT_EVENT_DEFAULT_DURATION; String propId = args[1]; String data = args[2]; // scan input for (int i = 3; i < args.length - 1; i++) { switch (args[i]) { case "-d": durationTime = args[++i]; break; case "-z" : areaId = args[++i]; break; case "-s" : sampleRate = args[++i]; break; default: writer.printf("%s is an invalid flag.\n", args[i]); showHelp(writer); return; } } try { float sampleRateFloat = Float.parseFloat(sampleRate); if (sampleRateFloat <= 0) { writer.printf("SampleRate: %s is an invalid value. " + "SampleRate must be greater than 0.\n", sampleRate); showHelp(writer); return; } mHal.injectContinuousVhalEvent(Integer.decode(propId), Integer.decode(areaId), data, sampleRateFloat, Long.parseLong(durationTime)); } catch (NumberFormatException e) { writer.printf("Invalid arguments: %s\n", e); showHelp(writer); } } private void setPropertyValue(String[] args, IndentingPrintWriter writer) { if (args.length != 4) { writer.println("Invalid command syntax:"); writer.printf("Usage: %s\n", getSetPropertyValueUsage()); return; } int propertyId = decodePropertyId(args[1]); int areaId = decodeAreaId(args[2]); String value = args[3]; Slogf.i(TAG, "Setting vehicle property ID= %s, areaId= %s, value= %s", toPropertyIdString(propertyId), toAreaIdString(propertyId, areaId), value); if (areaId == 0 && !isPropertyAreaTypeGlobal(propertyId)) { writer.printf("Property area type is inconsistent with given area ID: %s\n", toAreaIdString(propertyId, areaId)); return; } try { mHal.setPropertyFromCommand(propertyId, areaId, value, writer); writer.printf("Property %s area ID %s is set to %s successfully\n", toPropertyIdString(propertyId), toAreaIdString(propertyId, areaId), value); } catch (Exception e) { writer.printf("Cannot set a property: %s\n", e); } } private static String getSetPropertyValueUsage() { return COMMAND_SET_PROPERTY_VALUE + " " + ""; } // Set a target camera device for the rearview private void setRearviewCameraId(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } if (!mCarEvsService.setRearviewCameraIdFromCommand(args[1])) { writer.println("Failed to set CarEvsService rearview camera device id."); } else { writer.printf("CarEvsService is set to use %s.\n", args[1]); } } private void setCameraId(String[] args, IndentingPrintWriter writer) { if (args.length != 3) { showInvalidArguments(writer); return; } if (!mCarEvsService.setCameraIdFromCommand(args[1], args[2])) { writer.printf("Failed to set CarEvsService camera device id for %s.", args[1]); } else { writer.printf("CarEvsService is set to use %s for %s.\n", args[2], args[1]); } } private void enableCameraServiceType(String[] args, IndentingPrintWriter writer) { if (args.length != 3) { showInvalidArguments(writer); return; } if (!mCarEvsService.enableServiceTypeFromCommand(args[1], args[2])) { writer.printf("Failed to enable %s with a camera %s.\n", args[1], args[2]); return; } writer.printf("%s is successfully enabled and set to use a camera %s.\n", args[1], args[2]); } private void setDrivingSafetyRegion(String[] args, IndentingPrintWriter writer) { if (args.length != 1 && args.length != 2) { showInvalidArguments(writer); return; } String region = args.length == 2 ? args[1] : CarPackageManager.DRIVING_SAFETY_REGION_ALL; writer.println("Set driving safety region to:" + region); CarLocalServices.getService(CarPackageManagerService.class).resetDrivingSafetyRegion( region); } private void getRearviewCameraId(IndentingPrintWriter writer) { writer.printf("CarEvsService is using %s for the rearview.\n", mCarEvsService.getRearviewCameraIdFromCommand()); } private void getCameraId(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } writer.printf("CarEvsService is using %s for %s.\n", mCarEvsService.getCameraIdFromCommand(args[1]), args[1]); } private void checkCameraServiceTypeEnabled(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } if (!mCarEvsService.isServiceTypeEnabledFromCommand(args[1])) { writer.printf("%s is not enabled.\n", args[1]); return; } String cameraId = mCarEvsService.getCameraIdFromCommand(args[1]); writer.printf("%s is enabled and set to use %s.\n", args[1], cameraId); } private void controlWatchdogPackageKillableState(String[] args, IndentingPrintWriter writer) { if (args.length != 3) { showInvalidArguments(writer); return; } if (!Objects.equals(args[1], "true") && !Objects.equals(args[1], "false")) { writer.println("Failed to parse killable state argument. " + "Valid arguments: killable | not-killable"); return; } int currentUserId = ActivityManager.getCurrentUser(); mCarWatchdogService.setKillablePackageAsUser( args[2], UserHandle.of(currentUserId), Objects.equals(args[1], "true")); writer.printf("Set package killable state as '%s' for user '%d' and package '%s'\n", Objects.equals(args[1], "true") ? "killable" : "not killable", currentUserId, args[2]); } // Set third-party foreground I/O threshold for car watchdog private void setWatchdogIoThirdPartyForegroundBytes(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } try { long newForegroundModeBytes = Long.parseLong(args[1]); ResourceOveruseConfiguration configuration = getThirdPartyResourceOveruseConfiguration( CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO); if (configuration == null) { writer.println("Failed to get third-party resource overuse configurations."); return; } ResourceOveruseConfiguration newConfiguration = setComponentLevelForegroundIoBytes( configuration, newForegroundModeBytes); int result = mCarWatchdogService.setResourceOveruseConfigurations( Collections.singletonList(newConfiguration), CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO); if (result == CarWatchdogManager.RETURN_CODE_SUCCESS) { writer.printf( "Successfully set third-party I/O overuse foreground threshold. { " + "foregroundModeBytes = %d } \n", newForegroundModeBytes); } else { writer.println("Failed to set third-party I/O overuse foreground threshold."); } } catch (NumberFormatException e) { writer.println("The argument provided does not contain a parsable long."); writer.println("Failed to set third-party I/O overuse foreground threshold."); } catch (RemoteException e) { writer.printf("Failed to set third-party I/O overuse foreground threshold: %s", e.getMessage()); } } private void getWatchdogIoThirdPartyForegroundBytes(IndentingPrintWriter writer) { ResourceOveruseConfiguration configuration = getThirdPartyResourceOveruseConfiguration( CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO); try { IoOveruseConfiguration ioOveruseConfiguration = Objects.requireNonNull( configuration).getIoOveruseConfiguration(); PerStateBytes componentLevelThresholds = Objects.requireNonNull(ioOveruseConfiguration) .getComponentLevelThresholds(); long foregroundBytes = Objects.requireNonNull( componentLevelThresholds).getForegroundModeBytes(); writer.printf("foregroundModeBytes = %d \n", foregroundBytes); } catch (NullPointerException e) { writer.println("Failed to get third-party I/O overuse foreground threshold."); } } private ResourceOveruseConfiguration getThirdPartyResourceOveruseConfiguration( int resourceOveruseFlag) { for (ResourceOveruseConfiguration configuration : mCarWatchdogService.getResourceOveruseConfigurations(resourceOveruseFlag)) { if (configuration.getComponentType() == ResourceOveruseConfiguration.COMPONENT_TYPE_THIRD_PARTY) { return configuration; } } return null; } private ResourceOveruseConfiguration setComponentLevelForegroundIoBytes( ResourceOveruseConfiguration configuration, long foregroundModeBytes) { IoOveruseConfiguration ioOveruseConfiguration = configuration.getIoOveruseConfiguration(); PerStateBytes componentLevelThresholds = ioOveruseConfiguration.getComponentLevelThresholds(); return constructResourceOveruseConfigurationBuilder( configuration).setIoOveruseConfiguration( new IoOveruseConfiguration.Builder( new PerStateBytes(foregroundModeBytes, componentLevelThresholds.getBackgroundModeBytes(), componentLevelThresholds.getGarageModeBytes()), ioOveruseConfiguration.getPackageSpecificThresholds(), ioOveruseConfiguration.getAppCategorySpecificThresholds(), ioOveruseConfiguration.getSystemWideThresholds()) .build()) .build(); } private ResourceOveruseConfiguration.Builder constructResourceOveruseConfigurationBuilder( ResourceOveruseConfiguration configuration) { return new ResourceOveruseConfiguration.Builder(configuration.getComponentType(), configuration.getSafeToKillPackages(), configuration.getVendorPackagePrefixes(), configuration.getPackagesToAppCategoryTypes()) .setIoOveruseConfiguration(configuration.getIoOveruseConfiguration()); } private void controlWatchdogProcessHealthCheck(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } if (!Objects.equals(args[1], "enable") && !Objects.equals(args[1], "disable")) { writer.println("Failed to parse argument. Valid arguments: enable | disable"); return; } mCarWatchdogService.controlProcessHealthCheck(Objects.equals(args[1], "enable")); writer.printf("Watchdog health checking is now %sd \n", args[1]); } private void performResourceOveruseKill(String[] args, IndentingPrintWriter writer) { if (args.length != 2 && args.length != 4) { showInvalidArguments(writer); return; } String packageName = args[1]; int userId; if (args.length > 2 && Objects.equals(args[2], "--user")) { try { userId = Integer.parseInt(args[3]); } catch (NumberFormatException e) { writer.printf("Invalid user id provided: %s\n", args[3]); return; } } else { userId = ActivityManager.getCurrentUser(); } boolean isKilled = mCarWatchdogService.performResourceOveruseKill(packageName, userId); if (isKilled) { writer.printf("Successfully killed package '%s' for user %d\n", packageName, userId); } else { writer.printf("Failed to kill package '%s' for user %d\n", packageName, userId); } } private void printTelemetryHelp(IndentingPrintWriter writer) { writer.println("A CLI to interact with CarTelemetryService."); writer.println("\nUSAGE: adb shell cmd car_service telemetry [options]"); writer.println("\n\t-h"); writer.println("\t Print this help text."); writer.println("\tadd "); writer.println("\t Adds MetricsConfig from STDIN. Only a binary proto is supported."); writer.println("\tremove "); writer.println("\t Removes metrics config."); writer.println("\tremove-all"); writer.println("\t Removes all metrics configs."); writer.println("\tping-script-executor [published data filepath] [state filepath]"); writer.println("\t Runs a Lua script from stdin."); writer.println("\tlist"); writer.println("\t Lists the active config metrics."); writer.println("\tget-result --config --result-count --timeout " + " --print-results"); writer.println("\t Blocks until an expected number of metrics reports"); writer.println("\t is available and returns them."); writer.println("\t specifies the maximum number of seconds that"); writer.println("\t CLI will block for the expected "); writer.println("\t number of reports to come in."); writer.println("\t Optionally prints contents of every report received"); writer.println("\t if --print-results option is chosen."); writer.println("\nEXAMPLES:"); writer.println("\t$ adb shell cmd car_service telemetry add name < config1.protobin"); writer.println("\t\tWhere config1.protobin is a serialized MetricsConfig proto."); writer.println("\n\t$ adb shell cmd car_service telemetry get-result --config name " + "--result-count 1 --timeout 10 --print-results"); writer.println("\t$ adb shell cmd car_service telemetry ping-script-executor " + "< example_script.lua"); writer.println("\t$ adb shell cmd car_service telemetry ping-script-executor " + "/data/local/tmp/published_data < example_script.lua"); writer.println("\t$ adb shell cmd car_service telemetry ping-script-executor " + "/data/local/tmp/bundle /data/local/tmp/bundle2 < example_script.lua"); } private void handleTelemetryCommands(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { printTelemetryHelp(writer); return; } Car car = Car.createCar(mContext); CarTelemetryManager carTelemetryManager = (CarTelemetryManager) car.getCarManager(Car.CAR_TELEMETRY_SERVICE); if (carTelemetryManager == null) { writer.println("telemetry service is not enabled, cannot use CLI"); return; } String cmd = args[1]; switch (cmd) { case "add": if (args.length != 3) { writer.println("Invalid number of arguments."); printTelemetryHelp(writer); return; } try (BufferedInputStream in = new BufferedInputStream( new FileInputStream(getInFileDescriptor())); ByteArrayOutputStream out = new ByteArrayOutputStream()) { FileUtils.copy(in, out); CountDownLatch latch = new CountDownLatch(1); carTelemetryManager.addMetricsConfig(args[2], out.toByteArray(), Runnable::run, (metricsConfigName, statusCode) -> { if (statusCode == STATUS_ADD_METRICS_CONFIG_SUCCEEDED) { writer.printf("MetricsConfig %s is added.\n", args[2]); } else { writer.printf( "Failed to add %s. Status is %d. " + "Please see logcat for details.\n", args[2], statusCode); } latch.countDown(); }); latch.await(TELEMETRY_RESULT_WAIT_TIMEOUT.toSeconds(), TimeUnit.SECONDS); } catch (IOException | InterruptedException | NumberFormatException e) { writer.println("Failed to read from stdin: " + e); } break; case "remove": if (args.length != 3) { writer.println("Invalid number of arguments."); printTelemetryHelp(writer); return; } carTelemetryManager.removeMetricsConfig(args[2]); writer.printf("Removing %s... Please see logcat for details.\n", args[2]); break; case "remove-all": if (args.length != 2) { writer.println("Invalid number of arguments."); printTelemetryHelp(writer); return; } carTelemetryManager.removeAllMetricsConfigs(); writer.printf("Removing all MetricsConfigs... Please see logcat for details.\n"); break; case "ping-script-executor": if (args.length < 2 || args.length > 4) { writer.println("Invalid number of arguments."); printTelemetryHelp(writer); return; } PersistableBundle publishedData = new PersistableBundle(); publishedData.putInt("age", 99); publishedData.putStringArray( "string_array", new String[]{"a", "b", "c", "a", "b", "c", "a", "b", "c"}); PersistableBundle nestedBundle = new PersistableBundle(); nestedBundle.putInt("age", 100); nestedBundle.putStringArray( "string_array", new String[]{"q", "w", "e", "r", "t", "y"}); publishedData.putPersistableBundle("pers_bundle", nestedBundle); PersistableBundle savedState = null; // Read published data if (args.length >= 3) { try { publishedData = IoUtils.readBundle(new File(args[2])); } catch (IOException e) { writer.println("Published data path is invalid: " + e); return; } } // Read saved state if (args.length == 4) { try { savedState = IoUtils.readBundle(new File(args[3])); } catch (IOException e) { writer.println("Saved data path is invalid: " + e); return; } } try { pingScriptExecutor(writer, publishedData, savedState); } catch (InterruptedException | RemoteException e) { throw new RuntimeException(e); } break; case "list": writer.println("Active metric configs:"); mCarTelemetryService.getActiveMetricsConfigDetails().forEach((configDetails) -> { writer.printf("- %s\n", configDetails); }); break; case "get-result": if (args.length < 8 || args.length > 9) { writer.println("Invalid number of arguments."); printTelemetryHelp(writer); return; } String configName = null; String expectedResultCount = null; String timeout = null; boolean printResults = false; for (int i = 2; i < args.length; i++) { switch (args[i]) { case "--config": configName = args[++i]; break; case "--result-count": expectedResultCount = args[++i]; break; case "--timeout": timeout = args[++i]; break; case "--print-results": printResults = true; break; default: writer.printf("%s is an invalid argument.\n", args[i]); printTelemetryHelp(writer); return; } } if (configName == null) { writer.printf("--config value was not provided.\n"); printTelemetryHelp(writer); return; } if (expectedResultCount == null) { writer.printf("--result_count value was not provided.\n"); printTelemetryHelp(writer); return; } if (timeout == null) { writer.printf("--timeout value was not provided.\n"); printTelemetryHelp(writer); return; } CountDownLatch latch = new CountDownLatch(Integer.parseInt(expectedResultCount)); AtomicLong firstReportReady = new AtomicLong(Long.MIN_VALUE); AtomicLong lastReportReady = new AtomicLong(Long.MIN_VALUE); AtomicInteger numResultsReceived = new AtomicInteger(0); boolean shouldPrintResults = printResults; CarTelemetryManager.MetricsReportCallback callback = (metricsConfigName, report, telemetryError, status) -> { if (report != null) { // Captures the first time the callback is invoked. firstReportReady.compareAndSet(Long.MIN_VALUE, SystemClock.elapsedRealtime()); // The callback can be invoked many times. This variable stores // the time instant when the callback was called most recently. lastReportReady.set(SystemClock.elapsedRealtime()); report.size(); // unparcel()'s numResultsReceived.incrementAndGet(); if (shouldPrintResults) { writer.println("Report for " + metricsConfigName + ": " + report); } latch.countDown(); } else if (telemetryError != null) { parseTelemetryError(telemetryError, writer); } }; String parsedConfigName = configName; carTelemetryManager.clearReportReadyListener(); Executor executor = Executors.newSingleThreadExecutor(); carTelemetryManager.setReportReadyListener(executor, metricsConfigName -> { if (metricsConfigName.equals(parsedConfigName)) { carTelemetryManager.getFinishedReport(metricsConfigName, executor, callback); } }); try { writer.println("Waiting for the results..."); writer.flush(); latch.await(/* timeout =*/Integer.parseInt(timeout), TimeUnit.SECONDS); long delta = lastReportReady.get() - firstReportReady.get(); writer.println( "Took " + delta + " millis to produce " + numResultsReceived.get() + " reports"); writer.println("The first report produced at " + firstReportReady.get() + " millis since boot"); writer.println("The last report produced at " + lastReportReady.get() + " millis since boot"); writer.flush(); } catch (InterruptedException e) { writer.println("Result await error: " + e); } finally { carTelemetryManager.clearReportReadyListener(); } break; default: printTelemetryHelp(writer); } } private void pingScriptExecutor( IndentingPrintWriter writer, PersistableBundle publishedData, PersistableBundle savedState) throws InterruptedException, RemoteException { writer.println("Sending data to script executor..."); if (mScriptExecutor == null) { writer.println("[I] No mScriptExecutor, creating a new one"); connectToScriptExecutor(writer); } String script; try ( BufferedInputStream in = new BufferedInputStream( new FileInputStream(getInFileDescriptor())); ByteArrayOutputStream out = new ByteArrayOutputStream()) { FileUtils.copy(in, out); script = out.toString(); } catch (IOException | NumberFormatException e) { writer.println("[E] Failed to read from stdin: " + e); return; } writer.println("[I] Running the script: "); writer.println(script); writer.flush(); CountDownLatch resultLatch = new CountDownLatch(1); IScriptExecutorListener listener = new IScriptExecutorListener.Stub() { @Override public void onScriptFinished(PersistableBundle result) { writer.println("Script finished"); result.size(); // unparcel()'s writer.println("result: " + result); writer.flush(); resultLatch.countDown(); } @Override public void onSuccess(PersistableBundle state) { writer.println("Script succeeded, saving inter result"); state.size(); // unparcel()'s writer.println("state: " + state); writer.flush(); resultLatch.countDown(); } @Override public void onError(int errorType, String msg, String stack) { writer.println("Script error: " + errorType + ": " + msg); writer.println("Stack: " + stack); writer.flush(); resultLatch.countDown(); } @Override public void onMetricsReport( @NonNull PersistableBundle report, @Nullable PersistableBundle stateToPersist) { writer.println("Script produced a report without finishing"); report.size(); // unparcel()'s writer.println("report: " + report); if (stateToPersist != null) { stateToPersist.size(); // unparcel()'s writer.println("state to persist: " + stateToPersist); } writer.flush(); resultLatch.countDown(); } }; mScriptExecutor.invokeScript( script, "foo", publishedData, savedState, listener); writer.println("[I] Waiting for the result"); writer.flush(); resultLatch.await(10, TimeUnit.SECONDS); // seconds mContext.unbindService(mScriptExecutorConn); } private void connectToScriptExecutor(IndentingPrintWriter writer) throws InterruptedException { CountDownLatch connectionLatch = new CountDownLatch(1); mScriptExecutorConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { writer.println("[I] Connected to ScriptExecutor Service"); writer.flush(); mScriptExecutor = IScriptExecutor.Stub.asInterface(service); connectionLatch.countDown(); } @Override public void onServiceDisconnected(ComponentName name) { writer.println("[E] Failed to connect to ScriptExecutor Service"); writer.flush(); mScriptExecutor = null; connectionLatch.countDown(); } }; Intent intent = new Intent(); intent.setComponent( new ComponentName( "com.android.car.scriptexecutor", "com.android.car.scriptexecutor.ScriptExecutor")); writer.println("[I] Binding to the script executor"); boolean success = mContext.bindServiceAsUser( intent, mScriptExecutorConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); if (success) { writer.println("[I] Found ScriptExecutor package"); writer.flush(); } else { writer.println("[E] Failed to bind to ScriptExecutor"); writer.flush(); mScriptExecutor = null; if (mScriptExecutorConn != null) { mContext.unbindService(mScriptExecutorConn); } return; } writer.println("[I] Waiting for the connection"); connectionLatch.await(5, TimeUnit.SECONDS); // seconds } private void parseTelemetryError(byte[] telemetryError, IndentingPrintWriter writer) { try { TelemetryError error = TelemetryError.parseFrom(telemetryError); writer.println("Error: " + error.getErrorType().name() + ": " + error.getMessage()); } catch (IOException e) { writer.println("Error is received, but parsing error failed: " + e); } } private void controlComponentEnabledState(String[] args, IndentingPrintWriter writer) { if (args.length != 3) { showInvalidArguments(writer); return; } String packageName = args[2]; int currentUserId = ActivityManager.getCurrentUser(); if (Objects.equals(args[1], "get")) { try { int curState = PackageManagerHelper .getApplicationEnabledSettingForUser(packageName, currentUserId); writer.println("Current State: " + getAppEnabledStateName(curState)); } catch (Exception e) { writer.printf("%s: getting package enabled state failed with error: %s\n", TAG, e.toString()); } return; } int newState = 0; switch (args[1]) { case "default": newState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; break; case "enable": newState = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; break; case "disable_until_used": newState = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; break; default: writer.println("unsupported state action: " + args[1]); return; } String callingPackageName = mContext.getPackageManager().getNameForUid(Process.myUid()); try { PackageManagerHelper.setApplicationEnabledSettingForUser(packageName, newState, /* EnabledFlag */ 0, currentUserId, callingPackageName); } catch (Exception e) { writer.printf("%s: setting package enabled state failed with error: %s\n", TAG, e.toString()); return; } writer.println("New State: " + getAppEnabledStateName(newState)); } private String getAppEnabledStateName(int enabledState) { String stateName = "COMPONENT_ENABLED_STATE_"; switch (enabledState) { case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT: stateName += "DEFAULT"; break; case PackageManager.COMPONENT_ENABLED_STATE_ENABLED: stateName += "ENABLED"; break; case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: stateName += "DISABLED"; break; case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: stateName += "DISABLED_USER"; break; case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: stateName += "DISABLED_UNTIL_USED"; break; default: stateName += "UNSUPPORTED"; break; } return stateName; } private void checkLockIsSecure(String[] args, IndentingPrintWriter writer) { if ((args.length != 1) && (args.length != 2)) { showInvalidArguments(writer); } int userId = UserHandle.myUserId(); if (args.length == 2) { userId = Integer.parseInt(args[1]); } writer.println(LockPatternHelper.isSecure(mContext, userId)); } private void listVhalProps(IndentingPrintWriter writer) { // Note: The output here is used in AtsVehicleDeviceTest. DO NOT CHANGE the format without // updating AtsVehicleDeviceTest. writer.println("All supported property IDs from Vehicle HAL:"); List propIds = new ArrayList<>(); try { HalPropConfig[] configs = mHal.getAllPropConfigs(); for (int i = 0; i < configs.length; i++) { propIds.add(configs[i].getPropId()); } writer.println(propIds.toString()); } catch (RemoteException | ServiceSpecificException e) { writer.println("Failed to call getAllPropConfigs, exception: " + e); } } private void getVhalBackend(IndentingPrintWriter writer) { // Note: The output here is used in AtsVehicleDeviceTest. DO NOT CHANGE the format without // updating AtsVehicleDeviceTest. if (mHal.isAidlVhal()) { writer.println("Vehicle HAL backend: AIDL"); } else { writer.println("Vehicle HAL backend: HIDL"); } } private void testEchoReverseBytes(String[] args, IndentingPrintWriter writer) { // Note: The output here is used in // AndroidCarApiTest:android.car.apitest.VehicleHalLargeParcelableTest. // Do not change the output format without updating the test. if (args.length != 3) { showInvalidArguments(writer); return; } int propId = Integer.parseInt(args[1]); int requestSize = Integer.parseInt(args[2]); byte[] byteValues = new byte[requestSize]; for (int i = 0; i < requestSize; i++) { byteValues[i] = (byte) (i); } try { mHal.set(mHal.getHalPropValueBuilder().build(propId, /* areaId= */ 0, byteValues)); } catch (IllegalArgumentException e) { writer.println( "Test Skipped: The property: " + propId + " is not supported, error: " + e); return; } catch (ServiceSpecificException e) { writer.println( "Test Failed: Failed to set property: " + propId + ", error: " + e); return; } HalPropValue result; try { result = mHal.get(mHal.getHalPropValueBuilder().build(propId, /* areaId= */ 0)); } catch (IllegalArgumentException | ServiceSpecificException e) { writer.println( "Test Failed: Failed to get property: " + propId + ", error: " + e); return; } int resultSize = result.getByteValuesSize(); if (resultSize != requestSize) { writer.println("Test Failed: expect: " + requestSize + " bytes to be returned, got: " + resultSize); return; } byte[] reverse = new byte[requestSize]; for (int i = 0; i < requestSize; i++) { reverse[i] = byteValues[requestSize - 1 - i]; } byte[] resultValues = result.getByteArray(); if (!Arrays.equals(resultValues, reverse)) { writer.println("Test Failed: result mismatch, expect: " + Arrays.toString(reverse) + ", got: " + Arrays.toString(resultValues)); return; } try { // Set the property to a single byte to free-up memory. Cannot use empty byte array // here which would cause IllegalArgumentException. mHal.set(mHal.getHalPropValueBuilder().build(propId, /* areaId= */ 0, new byte[]{ 0x00 })); } catch (IllegalArgumentException | ServiceSpecificException e) { writer.println( "Test Failed: Failed to clean up property value: failed to set property: " + propId + ", error: " + e); return; } writer.println("Test Succeeded!"); } private void getTargetCarVersion(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { showInvalidArguments(writer); return; } int firstAppArg = 1; // TODO(b/234499460): move --user logic to private helper / support 'all' int userId = UserHandle.CURRENT.getIdentifier(); if (Objects.equals(args[1], "--user")) { if (args.length < 4) { showInvalidArguments(writer); return; } String userArg = args[2]; firstAppArg += 2; if (!Objects.equals(userArg, "current") && !Objects.equals(userArg, "cur")) { try { userId = Integer.parseInt(args[2]); } catch (NumberFormatException e) { showInvalidArguments(writer); return; } } } if (userId == UserHandle.CURRENT.getIdentifier()) { userId = ActivityManager.getCurrentUser(); } writer.printf("User %d:\n", userId); Context userContext = getContextForUser(userId); for (int i = firstAppArg; i < args.length; i++) { String app = args[i]; try { CarVersion Version = CarPackageManagerService.getTargetCarVersion( userContext, app); writer.printf(" %s: major=%d, minor=%d\n", app, Version.getMajorVersion(), Version.getMinorVersion()); } catch (ServiceSpecificException e) { if (e.errorCode == CarPackageManager.ERROR_CODE_NO_PACKAGE) { writer.printf(" %s: not found\n", app); } else { writer.printf(" %s: unexpected exception: %s \n", app, e); } continue; } } } private void setProcessGroup(String[] args, IndentingPrintWriter writer) { if (args.length != 3) { showInvalidArguments(writer); return; } int pid = Integer.parseInt(args[1]); int group = Integer.parseInt(args[2]); Slogf.d(TAG, "Setting process group for pid %d, group %d", pid, group); CarServiceHelperWrapper.getInstance().setProcessGroup(pid, group); writer.printf(" Successfully set pid %s to group %s\n", args[1], args[2]); } private void getProcessGroup(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } int pid = Integer.parseInt(args[1]); int group = CarServiceHelperWrapper.getInstance().getProcessGroup(pid); writer.printf("%d\n", group); } private void setProcessProfile(String[] args, IndentingPrintWriter writer) { if (args.length != 4) { showInvalidArguments(writer); return; } int pid = Integer.parseInt(args[1]); int uid = Integer.parseInt(args[2]); String profile = args[3]; Slogf.d(TAG, "Setting process profile for pid %d, uid %d, profile %s", pid, uid, profile); CarServiceHelperWrapper.getInstance().setProcessProfile(pid, uid, profile); writer.printf(" Successfully set pid %d uid %d to profile %s\n", pid, uid, profile); } private void getDisplayByUser(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } // TODO(b/234499460): move --user logic to private helper / support 'all' and 'current' String userIdArg = args[1]; int userId; if (Objects.equals(userIdArg, "current") || Objects.equals(userIdArg, "cur")) { userId = ActivityManager.getCurrentUser(); } else { try { userId = Integer.parseInt(userIdArg); } catch (NumberFormatException e) { writer.printf("Invalid user id: %s\n", userIdArg); return; } } int displayId = CarServiceHelperWrapper.getInstance().getMainDisplayAssignedToUser(userId); if (displayId == Display.INVALID_DISPLAY) { writer.println("none"); } else { writer.println(displayId); } } private void getUserByDisplay(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { showInvalidArguments(writer); return; } int displayId; String displayArg = args[1]; try { displayId = Integer.parseInt(displayArg); } catch (NumberFormatException e) { writer.printf("Invalid displayId id: %s\n", displayArg); return; } int userId = CarServiceHelperWrapper.getInstance().getUserAssignedToDisplay(displayId); if (userId == UserManagerHelper.USER_NULL) { writer.println("none"); return; } writer.println(userId); } private static boolean isPropertyAreaTypeGlobal(int propertyId) { return (propertyId & VehicleArea.MASK) == VehicleArea.GLOBAL; } private static String getSuspendCommandUsage(String command) { return command + " [" + PARAM_AUTO + "|" + PARAM_SIMULATE + "|" + PARAM_REAL + "] [" + PARAM_SKIP_GARAGEMODE + "] [" + PARAM_WAKEUP_AFTER + " RESUME_DELAY | " + PARAM_CANCEL_AFTER + " CANCEL_DELAY]" + "[" + PARAM_FREE_MEMORY + "]"; } private static final class AudioZoneMirrorStatusCallbackImpl extends IAudioZonesMirrorStatusCallback.Stub { private static final long TEST_CALLBACK_TIMEOUT_MS = 5_000; private int[] mZoneIds; private int mStatus; private CountDownLatch mStatusLatch = new CountDownLatch(1); @Override public void onAudioZonesMirrorStatusChanged(int[] zoneIds, int status) { mZoneIds = zoneIds; mStatus = status; mStatusLatch.countDown(); } private boolean waitForCallback() throws Exception { return mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); } public void reset() { mZoneIds = null; mStatus = 0; mStatusLatch = new CountDownLatch(1); } } }