1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.car; 17 18 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue.ASSOCIATE_CURRENT_USER; 19 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue.DISASSOCIATE_ALL_USERS; 20 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue.DISASSOCIATE_CURRENT_USER; 21 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType.CUSTOM_1; 22 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType.CUSTOM_2; 23 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType.CUSTOM_3; 24 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType.CUSTOM_4; 25 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType.KEY_FOB; 26 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.UserIdInt; 30 import android.app.ActivityManager; 31 import android.app.ActivityOptions; 32 import android.app.UiModeManager; 33 import android.car.Car; 34 import android.car.input.CarInputManager; 35 import android.car.input.RotaryEvent; 36 import android.car.user.CarUserManager; 37 import android.car.user.UserCreationResult; 38 import android.car.user.UserIdentificationAssociationResponse; 39 import android.car.user.UserRemovalResult; 40 import android.car.user.UserSwitchResult; 41 import android.car.userlib.HalCallback; 42 import android.car.userlib.UserHalHelper; 43 import android.content.ComponentName; 44 import android.content.Context; 45 import android.content.Intent; 46 import android.hardware.automotive.vehicle.V2_0.CreateUserRequest; 47 import android.hardware.automotive.vehicle.V2_0.CreateUserStatus; 48 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse; 49 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction; 50 import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest; 51 import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType; 52 import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest; 53 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus; 54 import android.hardware.automotive.vehicle.V2_0.UserFlags; 55 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociation; 56 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue; 57 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType; 58 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationValue; 59 import android.hardware.automotive.vehicle.V2_0.UserIdentificationGetRequest; 60 import android.hardware.automotive.vehicle.V2_0.UserIdentificationResponse; 61 import android.hardware.automotive.vehicle.V2_0.UserIdentificationSetAssociation; 62 import android.hardware.automotive.vehicle.V2_0.UserIdentificationSetRequest; 63 import android.hardware.automotive.vehicle.V2_0.UserInfo; 64 import android.hardware.automotive.vehicle.V2_0.UsersInfo; 65 import android.hardware.automotive.vehicle.V2_0.VehicleArea; 66 import android.os.Binder; 67 import android.os.Build; 68 import android.os.Process; 69 import android.os.ShellCommand; 70 import android.os.SystemClock; 71 import android.os.UserHandle; 72 import android.os.UserManager; 73 import android.text.TextUtils; 74 import android.util.ArrayMap; 75 import android.util.Log; 76 import android.util.SparseArray; 77 import android.view.KeyEvent; 78 79 import com.android.car.am.FixedActivityService; 80 import com.android.car.audio.CarAudioService; 81 import com.android.car.garagemode.GarageModeService; 82 import com.android.car.hal.InputHalService; 83 import com.android.car.hal.UserHalService; 84 import com.android.car.hal.VehicleHal; 85 import com.android.car.pm.CarPackageManagerService; 86 import com.android.car.systeminterface.SystemInterface; 87 import com.android.car.trust.CarTrustedDeviceService; 88 import com.android.car.user.CarUserService; 89 import com.android.internal.infra.AndroidFuture; 90 91 import java.io.PrintWriter; 92 import java.util.ArrayList; 93 import java.util.Arrays; 94 import java.util.List; 95 import java.util.concurrent.CountDownLatch; 96 import java.util.concurrent.TimeUnit; 97 import java.util.concurrent.atomic.AtomicBoolean; 98 99 final class CarShellCommand extends ShellCommand { 100 101 private static final String NO_INITIAL_USER = "N/A"; 102 103 private static final String TAG = CarShellCommand.class.getSimpleName(); 104 private static final boolean VERBOSE = false; 105 106 private static final String COMMAND_HELP = "-h"; 107 private static final String COMMAND_DAY_NIGHT_MODE = "day-night-mode"; 108 private static final String COMMAND_INJECT_VHAL_EVENT = "inject-vhal-event"; 109 private static final String COMMAND_INJECT_ERROR_EVENT = "inject-error-event"; 110 private static final String COMMAND_ENABLE_UXR = "enable-uxr"; 111 private static final String COMMAND_GARAGE_MODE = "garage-mode"; 112 private static final String COMMAND_GET_DO_ACTIVITIES = "get-do-activities"; 113 private static final String COMMAND_GET_CARPROPERTYCONFIG = "get-carpropertyconfig"; 114 private static final String COMMAND_GET_PROPERTY_VALUE = "get-property-value"; 115 private static final String COMMAND_PROJECTION_AP_TETHERING = "projection-tethering"; 116 private static final String COMMAND_PROJECTION_UI_MODE = "projection-ui-mode"; 117 private static final String COMMAND_RESUME = "resume"; 118 private static final String COMMAND_SUSPEND = "suspend"; 119 private static final String COMMAND_ENABLE_TRUSTED_DEVICE = "enable-trusted-device"; 120 private static final String COMMAND_REMOVE_TRUSTED_DEVICES = "remove-trusted-devices"; 121 private static final String COMMAND_SET_UID_TO_ZONE = "set-audio-zone-for-uid"; 122 private static final String COMMAND_START_FIXED_ACTIVITY_MODE = "start-fixed-activity-mode"; 123 private static final String COMMAND_STOP_FIXED_ACTIVITY_MODE = "stop-fixed-activity-mode"; 124 private static final String COMMAND_ENABLE_FEATURE = "enable-feature"; 125 private static final String COMMAND_DISABLE_FEATURE = "disable-feature"; 126 private static final String COMMAND_INJECT_KEY = "inject-key"; 127 private static final String COMMAND_INJECT_ROTARY = "inject-rotary"; 128 private static final String COMMAND_GET_INITIAL_USER_INFO = "get-initial-user-info"; 129 private static final String COMMAND_SWITCH_USER = "switch-user"; 130 private static final String COMMAND_REMOVE_USER = "remove-user"; 131 private static final String COMMAND_CREATE_USER = "create-user"; 132 private static final String COMMAND_GET_INITIAL_USER = "get-initial-user"; 133 private static final String COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE = 134 "set-occupant-zone-for-user"; 135 private static final String COMMAND_RESET_USER_ID_IN_OCCUPANT_ZONE = 136 "reset-user-in-occupant-zone"; 137 private static final String COMMAND_GET_USER_AUTH_ASSOCIATION = 138 "get-user-auth-association"; 139 private static final String COMMAND_SET_USER_AUTH_ASSOCIATION = 140 "set-user-auth-association"; 141 142 // Whitelist of commands allowed in user build. All these command should be protected with 143 // a permission. K: command, V: required permission. 144 // Only commands with permission already granted to shell user should be allowed. 145 // Commands that can affect safety should be never allowed in user build. 146 private static final ArrayMap<String, String> USER_BUILD_COMMAND_TO_PERMISSION_MAP; 147 static { 148 USER_BUILD_COMMAND_TO_PERMISSION_MAP = new ArrayMap<>(); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GARAGE_MODE, android.Manifest.permission.DEVICE_POWER)149 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GARAGE_MODE, 150 android.Manifest.permission.DEVICE_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_RESUME, android.Manifest.permission.DEVICE_POWER)151 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_RESUME, 152 android.Manifest.permission.DEVICE_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SUSPEND, android.Manifest.permission.DEVICE_POWER)153 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SUSPEND, 154 android.Manifest.permission.DEVICE_POWER); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_INITIAL_USER, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)155 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_INITIAL_USER, 156 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_INITIAL_USER_INFO, android.Manifest.permission.MANAGE_USERS)157 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_INITIAL_USER_INFO, 158 android.Manifest.permission.MANAGE_USERS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SWITCH_USER, android.Manifest.permission.MANAGE_USERS)159 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SWITCH_USER, 160 android.Manifest.permission.MANAGE_USERS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_REMOVE_USER, android.Manifest.permission.MANAGE_USERS)161 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_REMOVE_USER, 162 android.Manifest.permission.MANAGE_USERS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_CREATE_USER, android.Manifest.permission.MANAGE_USERS)163 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_CREATE_USER, 164 android.Manifest.permission.MANAGE_USERS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_USER_AUTH_ASSOCIATION, android.Manifest.permission.MANAGE_USERS)165 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_USER_AUTH_ASSOCIATION, 166 android.Manifest.permission.MANAGE_USERS); USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SET_USER_AUTH_ASSOCIATION, android.Manifest.permission.MANAGE_USERS)167 USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SET_USER_AUTH_ASSOCIATION, 168 android.Manifest.permission.MANAGE_USERS); 169 } 170 171 private static final String DEVICE_POWER_PERMISSION = "android.permission.DEVICE_POWER"; 172 173 private static final String PARAM_DAY_MODE = "day"; 174 private static final String PARAM_NIGHT_MODE = "night"; 175 private static final String PARAM_SENSOR_MODE = "sensor"; 176 private static final String PARAM_VEHICLE_PROPERTY_AREA_GLOBAL = "0"; 177 private static final String PARAM_ON_MODE = "on"; 178 private static final String PARAM_OFF_MODE = "off"; 179 private static final String PARAM_QUERY_MODE = "query"; 180 private static final String PARAM_REBOOT = "reboot"; 181 182 private static final int RESULT_OK = 0; 183 private static final int RESULT_ERROR = -1; // Arbitrary value, any non-0 is fine 184 185 private static final int DEFAULT_HAL_TIMEOUT_MS = 1_000; 186 187 private static final int INVALID_USER_AUTH_TYPE_OR_VALUE = -1; 188 189 private static final SparseArray<String> VALID_USER_AUTH_TYPES; 190 private static final String VALID_USER_AUTH_TYPES_HELP; 191 192 private static final SparseArray<String> VALID_USER_AUTH_SET_VALUES; 193 private static final String VALID_USER_AUTH_SET_VALUES_HELP; 194 195 static { 196 VALID_USER_AUTH_TYPES = new SparseArray<String>(5); VALID_USER_AUTH_TYPES.put(KEY_FOB, UserIdentificationAssociationType.toString(KEY_FOB))197 VALID_USER_AUTH_TYPES.put(KEY_FOB, UserIdentificationAssociationType.toString(KEY_FOB)); VALID_USER_AUTH_TYPES.put(CUSTOM_1, UserIdentificationAssociationType.toString(CUSTOM_1))198 VALID_USER_AUTH_TYPES.put(CUSTOM_1, UserIdentificationAssociationType.toString(CUSTOM_1)); VALID_USER_AUTH_TYPES.put(CUSTOM_2, UserIdentificationAssociationType.toString(CUSTOM_2))199 VALID_USER_AUTH_TYPES.put(CUSTOM_2, UserIdentificationAssociationType.toString(CUSTOM_2)); VALID_USER_AUTH_TYPES.put(CUSTOM_3, UserIdentificationAssociationType.toString(CUSTOM_3))200 VALID_USER_AUTH_TYPES.put(CUSTOM_3, UserIdentificationAssociationType.toString(CUSTOM_3)); VALID_USER_AUTH_TYPES.put(CUSTOM_4, UserIdentificationAssociationType.toString(CUSTOM_4))201 VALID_USER_AUTH_TYPES.put(CUSTOM_4, UserIdentificationAssociationType.toString(CUSTOM_4)); 202 VALID_USER_AUTH_TYPES_HELP = getHelpString("types", VALID_USER_AUTH_TYPES); 203 204 VALID_USER_AUTH_SET_VALUES = new SparseArray<String>(3); VALID_USER_AUTH_SET_VALUES.put(ASSOCIATE_CURRENT_USER, UserIdentificationAssociationSetValue.toString(ASSOCIATE_CURRENT_USER))205 VALID_USER_AUTH_SET_VALUES.put(ASSOCIATE_CURRENT_USER, 206 UserIdentificationAssociationSetValue.toString(ASSOCIATE_CURRENT_USER)); VALID_USER_AUTH_SET_VALUES.put(DISASSOCIATE_CURRENT_USER, UserIdentificationAssociationSetValue.toString(DISASSOCIATE_CURRENT_USER))207 VALID_USER_AUTH_SET_VALUES.put(DISASSOCIATE_CURRENT_USER, 208 UserIdentificationAssociationSetValue.toString(DISASSOCIATE_CURRENT_USER)); VALID_USER_AUTH_SET_VALUES.put(DISASSOCIATE_ALL_USERS, UserIdentificationAssociationSetValue.toString(DISASSOCIATE_ALL_USERS))209 VALID_USER_AUTH_SET_VALUES.put(DISASSOCIATE_ALL_USERS, 210 UserIdentificationAssociationSetValue.toString(DISASSOCIATE_ALL_USERS)); 211 VALID_USER_AUTH_SET_VALUES_HELP = getHelpString("values", VALID_USER_AUTH_SET_VALUES); 212 } 213 214 @NonNull getHelpString(@onNull String name, @NonNull SparseArray<String> values)215 private static String getHelpString(@NonNull String name, @NonNull SparseArray<String> values) { 216 StringBuilder help = new StringBuilder("Valid ").append(name).append(" are: "); 217 int size = values.size(); 218 for (int i = 0; i < size; i++) { 219 help.append(values.valueAt(i)); 220 if (i != size - 1) { 221 help.append(", "); 222 } 223 } 224 return help.append('.').toString(); 225 } 226 227 private final Context mContext; 228 private final VehicleHal mHal; 229 private final CarAudioService mCarAudioService; 230 private final CarPackageManagerService mCarPackageManagerService; 231 private final CarProjectionService mCarProjectionService; 232 private final CarPowerManagementService mCarPowerManagementService; 233 private final CarTrustedDeviceService mCarTrustedDeviceService; 234 private final FixedActivityService mFixedActivityService; 235 private final CarFeatureController mFeatureController; 236 private final CarInputService mCarInputService; 237 private final CarNightService mCarNightService; 238 private final SystemInterface mSystemInterface; 239 private final GarageModeService mGarageModeService; 240 private final CarUserService mCarUserService; 241 private final CarOccupantZoneService mCarOccupantZoneService; 242 CarShellCommand(Context context, VehicleHal hal, CarAudioService carAudioService, CarPackageManagerService carPackageManagerService, CarProjectionService carProjectionService, CarPowerManagementService carPowerManagementService, CarTrustedDeviceService carTrustedDeviceService, FixedActivityService fixedActivityService, CarFeatureController featureController, CarInputService carInputService, CarNightService carNightService, SystemInterface systemInterface, GarageModeService garageModeService, CarUserService carUserService, CarOccupantZoneService carOccupantZoneService)243 CarShellCommand(Context context, 244 VehicleHal hal, 245 CarAudioService carAudioService, 246 CarPackageManagerService carPackageManagerService, 247 CarProjectionService carProjectionService, 248 CarPowerManagementService carPowerManagementService, 249 CarTrustedDeviceService carTrustedDeviceService, 250 FixedActivityService fixedActivityService, 251 CarFeatureController featureController, 252 CarInputService carInputService, 253 CarNightService carNightService, 254 SystemInterface systemInterface, 255 GarageModeService garageModeService, 256 CarUserService carUserService, 257 CarOccupantZoneService carOccupantZoneService) { 258 mContext = context; 259 mHal = hal; 260 mCarAudioService = carAudioService; 261 mCarPackageManagerService = carPackageManagerService; 262 mCarProjectionService = carProjectionService; 263 mCarPowerManagementService = carPowerManagementService; 264 mCarTrustedDeviceService = carTrustedDeviceService; 265 mFixedActivityService = fixedActivityService; 266 mFeatureController = featureController; 267 mCarInputService = carInputService; 268 mCarNightService = carNightService; 269 mSystemInterface = systemInterface; 270 mGarageModeService = garageModeService; 271 mCarUserService = carUserService; 272 mCarOccupantZoneService = carOccupantZoneService; 273 } 274 275 @Override onCommand(String cmd)276 public int onCommand(String cmd) { 277 if (cmd == null) { 278 onHelp(); 279 return RESULT_ERROR; 280 } 281 ArrayList<String> argsList = new ArrayList<>(); 282 argsList.add(cmd); 283 String arg = null; 284 do { 285 arg = getNextArg(); 286 if (arg != null) { 287 argsList.add(arg); 288 } 289 } while (arg != null); 290 String[] args = new String[argsList.size()]; 291 argsList.toArray(args); 292 return exec(args, getOutPrintWriter()); 293 } 294 295 @Override onHelp()296 public void onHelp() { 297 showHelp(getOutPrintWriter()); 298 } 299 showHelp(PrintWriter pw)300 private static void showHelp(PrintWriter pw) { 301 pw.println("Car service commands:"); 302 pw.println("\t-h"); 303 pw.println("\t Print this help text."); 304 pw.println("\tday-night-mode [day|night|sensor]"); 305 pw.println("\t Force into day/night mode or restore to auto."); 306 pw.println("\tinject-vhal-event property [zone] data(can be comma separated list) " 307 + "[-t delay_time_seconds]"); 308 pw.println("\t Inject a vehicle property for testing."); 309 pw.println("\t delay_time_seconds: the event timestamp is increased by certain second."); 310 pw.println("\t If not specified, it will be 0."); 311 pw.println("\tinject-error-event property zone errorCode"); 312 pw.println("\t Inject an error event from VHAL for testing."); 313 pw.println("\tenable-uxr true|false"); 314 pw.println("\t Enable/Disable UX restrictions and App blocking."); 315 pw.println("\tgarage-mode [on|off|query|reboot]"); 316 pw.println("\t Force into or out of garage mode, or check status."); 317 pw.println("\t With 'reboot', enter garage mode, then reboot when it completes."); 318 pw.println("\tget-do-activities pkgname"); 319 pw.println("\t Get Distraction Optimized activities in given package."); 320 pw.println("\tget-carpropertyconfig [propertyId]"); 321 pw.println("\t Get a CarPropertyConfig by Id in Hex or list all CarPropertyConfigs"); 322 pw.println("\tget-property-value [propertyId] [areaId]"); 323 pw.println("\t Get a vehicle property value by property id in Hex and areaId"); 324 pw.println("\t or list all property values for all areaId"); 325 pw.println("\tsuspend"); 326 pw.println("\t Suspend the system to Deep Sleep."); 327 pw.println("\tresume"); 328 pw.println("\t Wake the system up after a 'suspend.'"); 329 pw.println("\tenable-trusted-device true|false"); 330 pw.println("\t Enable/Disable Trusted device feature."); 331 pw.println("\tremove-trusted-devices"); 332 pw.println("\t Remove all trusted devices for the current foreground user."); 333 pw.println("\tprojection-tethering [true|false]"); 334 pw.println("\t Whether tethering should be used when creating access point for" 335 + " wireless projection"); 336 pw.println("\t--metrics"); 337 pw.println("\t When used with dumpsys, only metrics will be in the dumpsys output."); 338 pw.printf("\t%s [zoneid] [uid]\n", COMMAND_SET_UID_TO_ZONE); 339 pw.println("\t Maps the audio zoneid to uid."); 340 pw.println("\tstart-fixed-activity displayId packageName activityName"); 341 pw.println("\t Start an Activity the specified display as fixed mode"); 342 pw.println("\tstop-fixed-mode displayId"); 343 pw.println("\t Stop fixed Activity mode for the given display. " 344 + "The Activity will not be restarted upon crash."); 345 pw.println("\tenable-feature featureName"); 346 pw.println("\t Enable the requested feature. Change will happen after reboot."); 347 pw.println("\t This requires root/su."); 348 pw.println("\tdisable-feature featureName"); 349 pw.println("\t Disable the requested feature. Change will happen after reboot"); 350 pw.println("\t This requires root/su."); 351 pw.println("\tinject-key [-d display] [-t down_delay_ms] key_code"); 352 pw.println("\t inject key down / up event to car service"); 353 pw.println("\t display: 0 for main, 1 for cluster. If not specified, it will be 0."); 354 pw.println("\t down_delay_ms: delay from down to up key event. If not specified,"); 355 pw.println("\t it will be 0"); 356 pw.println("\t key_code: int key code defined in android KeyEvent"); 357 pw.println("\tinject-rotary [-d display] [-i input_type] [-c clockwise]"); 358 pw.println("\t [-dt delta_times_ms]"); 359 pw.println("\t inject rotary input event to car service."); 360 pw.println("\t display: 0 for main, 1 for cluster. If not specified, it will be 0."); 361 pw.println("\t input_type: 10 for navigation controller input, 11 for volume"); 362 pw.println("\t controller input. If not specified, it will be 10."); 363 pw.println("\t clockwise: true if the event is clockwise, false if the event is"); 364 pw.println("\t counter-clockwise. If not specified, it will be false."); 365 pw.println("\t delta_times_ms: a list of delta time (current time minus event time)"); 366 pw.println("\t in descending order. If not specified, it will be 0."); 367 368 pw.printf("\t%s <REQ_TYPE> [--hal-only] [--timeout TIMEOUT_MS]\n", 369 COMMAND_GET_INITIAL_USER_INFO); 370 pw.println("\t Calls the Vehicle HAL to get the initial boot info, passing the given"); 371 pw.println("\t REQ_TYPE (which could be either FIRST_BOOT, FIRST_BOOT_AFTER_OTA, "); 372 pw.println("\t COLD_BOOT, RESUME, or any numeric value that would be passed 'as-is')"); 373 pw.println("\t and an optional TIMEOUT_MS to wait for the HAL response (if not set,"); 374 pw.println("\t it will use a default value)."); 375 pw.println("\t The --hal-only option only calls HAL, without using CarUserService."); 376 377 pw.printf("\t%s <USER_ID> [--hal-only] [--timeout TIMEOUT_MS]\n", COMMAND_SWITCH_USER); 378 pw.println("\t Switches to user USER_ID using the HAL integration."); 379 pw.println("\t The --hal-only option only calls HAL, without switching the user,"); 380 pw.println("\t while the --timeout defines how long to wait for the HAL response."); 381 382 pw.printf("\t%s <USER_ID> [--hal-only]\n", COMMAND_REMOVE_USER); 383 pw.println("\t Removes user with USER_ID using the HAL integration."); 384 pw.println("\t The --hal-only option only calls HAL, without removing the user,"); 385 386 pw.printf("\t%s [--hal-only] [--timeout TIMEOUT_MS] [--type TYPE] [--flags FLAGS] [NAME]\n", 387 COMMAND_CREATE_USER); 388 pw.println("\t Creates a new user using the HAL integration."); 389 pw.println("\t The --hal-only uses UserManager to create the user,"); 390 pw.println("\t while the --timeout defines how long to wait for the HAL response."); 391 392 pw.printf("\t%s\n", COMMAND_GET_INITIAL_USER); 393 pw.printf("\t Gets the id of the initial user (or %s when it's not available)\n", 394 NO_INITIAL_USER); 395 396 pw.printf("\t%s [occupantZoneId] [userId]\n", COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE); 397 pw.println("\t Maps the occupant zone id to user id."); 398 pw.printf("\t%s [occupantZoneId]\n", COMMAND_RESET_USER_ID_IN_OCCUPANT_ZONE); 399 pw.println("\t Unmaps the user assigned to occupant zone id."); 400 401 pw.printf("\t%s [--hal-only] [--user USER_ID] TYPE1 [..TYPE_N]\n", 402 COMMAND_GET_USER_AUTH_ASSOCIATION); 403 pw.println("\t Gets the N user authentication values for the N types for the given user"); 404 pw.println("\t (or current user when not specified)."); 405 pw.println("\t By defautt it calls CarUserManager, but using --hal-only will call just " 406 + "UserHalService."); 407 408 pw.printf("\t%s [--hal-only] [--user USER_ID] TYPE1 VALUE1 [..TYPE_N VALUE_N]\n", 409 COMMAND_SET_USER_AUTH_ASSOCIATION); 410 pw.println("\t Sets the N user authentication types with the N values for the given user"); 411 pw.println("\t (or current user when not specified)."); 412 pw.println("\t By defautt it calls CarUserManager, but using --hal-only will call just " 413 + "UserHalService."); 414 415 pw.printf("\t %s\n", VALID_USER_AUTH_TYPES_HELP); 416 pw.printf("\t %s\n", VALID_USER_AUTH_SET_VALUES_HELP); 417 } 418 showInvalidArguments(PrintWriter pw)419 private static int showInvalidArguments(PrintWriter pw) { 420 pw.println("Incorrect number of arguments."); 421 showHelp(pw); 422 return RESULT_ERROR; 423 } 424 runSetZoneIdForUid(String zoneString, String uidString)425 private void runSetZoneIdForUid(String zoneString, String uidString) { 426 int uid = Integer.parseInt(uidString); 427 int zoneId = Integer.parseInt(zoneString); 428 mCarAudioService.setZoneIdForUid(zoneId, uid); 429 } 430 runSetOccupantZoneIdForUserId(String occupantZoneIdString, String userIdString)431 private void runSetOccupantZoneIdForUserId(String occupantZoneIdString, 432 String userIdString) { 433 int userId = Integer.parseInt(userIdString); 434 int occupantZoneId = Integer.parseInt(occupantZoneIdString); 435 if (!mCarOccupantZoneService.assignProfileUserToOccupantZone(occupantZoneId, userId)) { 436 throw new IllegalStateException("Failed to set userId " + userId + " to occupantZoneId " 437 + occupantZoneIdString); 438 } 439 } 440 runResetOccupantZoneId(String occupantZoneIdString)441 private void runResetOccupantZoneId(String occupantZoneIdString) { 442 int occupantZoneId = Integer.parseInt(occupantZoneIdString); 443 if (!mCarOccupantZoneService 444 .assignProfileUserToOccupantZone(occupantZoneId, UserHandle.USER_NULL)) { 445 throw new IllegalStateException("Failed to reset occupantZoneId " 446 + occupantZoneIdString); 447 } 448 } 449 exec(String[] args, PrintWriter writer)450 int exec(String[] args, PrintWriter writer) { 451 String cmd = args[0]; 452 String requiredPermission = USER_BUILD_COMMAND_TO_PERMISSION_MAP.get(cmd); 453 if (VERBOSE) { 454 Log.v(TAG, "cmd: " + cmd + ", requiredPermission: " + requiredPermission); 455 } 456 if (Build.IS_USER && requiredPermission == null) { 457 throw new SecurityException("The command " + cmd + "requires non-user build"); 458 } 459 if (requiredPermission != null) { 460 if (!ICarImpl.hasPermission(mContext, requiredPermission)) { 461 throw new SecurityException("The command " + cmd + "requires permission:" 462 + requiredPermission); 463 } 464 } 465 switch (cmd) { 466 case COMMAND_HELP: 467 showHelp(writer); 468 break; 469 case COMMAND_DAY_NIGHT_MODE: { 470 String value = args.length < 2 ? "" : args[1]; 471 forceDayNightMode(value, writer); 472 break; 473 } 474 case COMMAND_GARAGE_MODE: { 475 String value = args.length < 2 ? "" : args[1]; 476 forceGarageMode(value, writer); 477 break; 478 } 479 case COMMAND_INJECT_VHAL_EVENT: 480 String zone = PARAM_VEHICLE_PROPERTY_AREA_GLOBAL; 481 String data; 482 int argNum = args.length; 483 if (argNum < 3 || argNum > 6) { 484 return showInvalidArguments(writer); 485 } 486 String delayTime = args[argNum - 2].equals("-t") ? args[argNum - 1] : "0"; 487 if (argNum == 4 || argNum == 6) { 488 // Zoned 489 zone = args[2]; 490 data = args[3]; 491 } else { 492 // Global 493 data = args[2]; 494 } 495 injectVhalEvent(args[1], zone, data, false, delayTime, writer); 496 break; 497 case COMMAND_INJECT_ERROR_EVENT: 498 if (args.length != 4) { 499 return showInvalidArguments(writer); 500 } 501 String errorAreaId = args[2]; 502 String errorCode = args[3]; 503 injectVhalEvent(args[1], errorAreaId, errorCode, true, "0", writer); 504 break; 505 case COMMAND_ENABLE_UXR: 506 if (args.length != 2) { 507 return showInvalidArguments(writer); 508 } 509 boolean enableBlocking = Boolean.valueOf(args[1]); 510 if (mCarPackageManagerService != null) { 511 mCarPackageManagerService.setEnableActivityBlocking(enableBlocking); 512 } 513 break; 514 case COMMAND_GET_DO_ACTIVITIES: 515 if (args.length != 2) { 516 return showInvalidArguments(writer); 517 } 518 String pkgName = args[1].toLowerCase(); 519 if (mCarPackageManagerService != null) { 520 String[] doActivities = 521 mCarPackageManagerService.getDistractionOptimizedActivities( 522 pkgName); 523 if (doActivities != null) { 524 writer.println("DO Activities for " + pkgName); 525 for (String a : doActivities) { 526 writer.println(a); 527 } 528 } else { 529 writer.println("No DO Activities for " + pkgName); 530 } 531 } 532 break; 533 case COMMAND_GET_CARPROPERTYCONFIG: 534 String propertyId = args.length < 2 ? "" : args[1]; 535 mHal.dumpPropertyConfigs(writer, propertyId); 536 break; 537 case COMMAND_GET_PROPERTY_VALUE: 538 String propId = args.length < 2 ? "" : args[1]; 539 String areaId = args.length < 3 ? "" : args[2]; 540 mHal.dumpPropertyValueByCommend(writer, propId, areaId); 541 break; 542 case COMMAND_PROJECTION_UI_MODE: 543 if (args.length != 2) { 544 return showInvalidArguments(writer); 545 } 546 mCarProjectionService.setUiMode(Integer.valueOf(args[1])); 547 break; 548 case COMMAND_PROJECTION_AP_TETHERING: 549 if (args.length != 2) { 550 return showInvalidArguments(writer); 551 } 552 mCarProjectionService.setAccessPointTethering(Boolean.valueOf(args[1])); 553 break; 554 case COMMAND_RESUME: 555 mCarPowerManagementService.forceSimulatedResume(); 556 writer.println("Resume: Simulating resuming from Deep Sleep"); 557 break; 558 case COMMAND_SUSPEND: 559 mCarPowerManagementService.forceSuspendAndMaybeReboot(false); 560 writer.println("Resume: Simulating powering down to Deep Sleep"); 561 break; 562 case COMMAND_ENABLE_TRUSTED_DEVICE: 563 if (args.length != 2) { 564 return showInvalidArguments(writer); 565 } 566 mCarTrustedDeviceService.getCarTrustAgentEnrollmentService() 567 .setTrustedDeviceEnrollmentEnabled(Boolean.valueOf(args[1])); 568 mCarTrustedDeviceService.getCarTrustAgentUnlockService() 569 .setTrustedDeviceUnlockEnabled(Boolean.valueOf(args[1])); 570 break; 571 case COMMAND_REMOVE_TRUSTED_DEVICES: 572 mCarTrustedDeviceService.getCarTrustAgentEnrollmentService() 573 .removeAllTrustedDevices(ActivityManager.getCurrentUser()); 574 break; 575 case COMMAND_SET_UID_TO_ZONE: 576 if (args.length != 3) { 577 return showInvalidArguments(writer); 578 } 579 runSetZoneIdForUid(args[1], args[2]); 580 break; 581 case COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE: 582 if (args.length != 3) { 583 return showInvalidArguments(writer); 584 } 585 runSetOccupantZoneIdForUserId(args[1], args[2]); 586 break; 587 case COMMAND_RESET_USER_ID_IN_OCCUPANT_ZONE: 588 if (args.length != 2) { 589 return showInvalidArguments(writer); 590 } 591 runResetOccupantZoneId(args[1]); 592 break; 593 case COMMAND_START_FIXED_ACTIVITY_MODE: 594 startFixedActivity(args, writer); 595 break; 596 case COMMAND_STOP_FIXED_ACTIVITY_MODE: 597 stopFixedMode(args, writer); 598 break; 599 case COMMAND_ENABLE_FEATURE: 600 if (args.length != 2) { 601 return showInvalidArguments(writer); 602 } 603 enableDisableFeature(args, writer, /* enable= */ true); 604 break; 605 case COMMAND_DISABLE_FEATURE: 606 if (args.length != 2) { 607 return showInvalidArguments(writer); 608 } 609 enableDisableFeature(args, writer, /* enable= */ false); 610 break; 611 case COMMAND_INJECT_KEY: 612 if (args.length < 2) { 613 return showInvalidArguments(writer); 614 } 615 injectKey(args, writer); 616 break; 617 case COMMAND_INJECT_ROTARY: 618 if (args.length < 1) { 619 return showInvalidArguments(writer); 620 } 621 injectRotary(args, writer); 622 break; 623 case COMMAND_GET_INITIAL_USER_INFO: 624 getInitialUserInfo(args, writer); 625 break; 626 case COMMAND_SWITCH_USER: 627 switchUser(args, writer); 628 break; 629 case COMMAND_REMOVE_USER: 630 removeUser(args, writer); 631 break; 632 case COMMAND_CREATE_USER: 633 createUser(args, writer); 634 break; 635 case COMMAND_GET_INITIAL_USER: 636 getInitialUser(writer); 637 break; 638 case COMMAND_GET_USER_AUTH_ASSOCIATION: 639 getUserAuthAssociation(args, writer); 640 break; 641 case COMMAND_SET_USER_AUTH_ASSOCIATION: 642 setUserAuthAssociation(args, writer); 643 break; 644 default: 645 writer.println("Unknown command: \"" + cmd + "\""); 646 showHelp(writer); 647 return RESULT_ERROR; 648 } 649 return RESULT_OK; 650 } 651 652 private void startFixedActivity(String[] args, PrintWriter writer) { 653 if (args.length != 4) { 654 writer.println("Incorrect number of arguments"); 655 showHelp(writer); 656 return; 657 } 658 int displayId; 659 try { 660 displayId = Integer.parseInt(args[1]); 661 } catch (NumberFormatException e) { 662 writer.println("Wrong display id:" + args[1]); 663 return; 664 } 665 String packageName = args[2]; 666 String activityName = args[3]; 667 Intent intent = new Intent(); 668 intent.setComponent(new ComponentName(packageName, activityName)); 669 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 670 ActivityOptions options = ActivityOptions.makeBasic(); 671 options.setLaunchDisplayId(displayId); 672 if (!mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent, options, 673 displayId, ActivityManager.getCurrentUser())) { 674 writer.println("Failed to start"); 675 return; 676 } 677 writer.println("Succeeded"); 678 } 679 680 private void stopFixedMode(String[] args, PrintWriter writer) { 681 if (args.length != 2) { 682 writer.println("Incorrect number of arguments"); 683 showHelp(writer); 684 return; 685 } 686 int displayId; 687 try { 688 displayId = Integer.parseInt(args[1]); 689 } catch (NumberFormatException e) { 690 writer.println("Wrong display id:" + args[1]); 691 return; 692 } 693 mFixedActivityService.stopFixedActivityMode(displayId); 694 } 695 696 private void enableDisableFeature(String[] args, PrintWriter writer, boolean enable) { 697 if (Binder.getCallingUid() != Process.ROOT_UID) { 698 writer.println("Only allowed to root/su"); 699 return; 700 } 701 String featureName = args[1]; 702 long id = Binder.clearCallingIdentity(); 703 // no permission check here 704 int r; 705 if (enable) { 706 r = mFeatureController.enableFeature(featureName); 707 } else { 708 r = mFeatureController.disableFeature(featureName); 709 } 710 switch (r) { 711 case Car.FEATURE_REQUEST_SUCCESS: 712 if (enable) { 713 writer.println("Enabled feature:" + featureName); 714 } else { 715 writer.println("Disabled feature:" + featureName); 716 } 717 break; 718 case Car.FEATURE_REQUEST_ALREADY_IN_THE_STATE: 719 if (enable) { 720 writer.println("Already enabled:" + featureName); 721 } else { 722 writer.println("Already disabled:" + featureName); 723 } 724 break; 725 case Car.FEATURE_REQUEST_MANDATORY: 726 writer.println("Cannot change mandatory feature:" + featureName); 727 break; 728 case Car.FEATURE_REQUEST_NOT_EXISTING: 729 writer.println("Non-existing feature:" + featureName); 730 break; 731 default: 732 writer.println("Unknown error:" + r); 733 break; 734 } 735 Binder.restoreCallingIdentity(id); 736 } 737 738 private void injectKey(String[] args, PrintWriter writer) { 739 int i = 1; // 0 is command itself 740 int display = InputHalService.DISPLAY_MAIN; 741 int delayMs = 0; 742 int keyCode = KeyEvent.KEYCODE_UNKNOWN; 743 try { 744 while (i < args.length) { 745 switch (args[i]) { 746 case "-d": 747 i++; 748 display = Integer.parseInt(args[i]); 749 break; 750 case "-t": 751 i++; 752 delayMs = Integer.parseInt(args[i]); 753 break; 754 default: 755 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 756 throw new IllegalArgumentException("key_code already set:" 757 + keyCode); 758 } 759 keyCode = Integer.parseInt(args[i]); 760 } 761 i++; 762 } 763 } catch (Exception e) { 764 writer.println("Invalid args:" + e); 765 showHelp(writer); 766 return; 767 } 768 if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { 769 writer.println("Missing key code or invalid keycode"); 770 showHelp(writer); 771 return; 772 } 773 if (display != InputHalService.DISPLAY_MAIN 774 && display != InputHalService.DISPLAY_INSTRUMENT_CLUSTER) { 775 writer.println("Invalid display:" + display); 776 showHelp(writer); 777 return; 778 } 779 if (delayMs < 0) { 780 writer.println("Invalid delay:" + delayMs); 781 showHelp(writer); 782 783 return; 784 } 785 KeyEvent keyDown = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); 786 mCarInputService.onKeyEvent(keyDown, display); 787 SystemClock.sleep(delayMs); 788 KeyEvent keyUp = new KeyEvent(KeyEvent.ACTION_UP, keyCode); 789 mCarInputService.onKeyEvent(keyUp, display); 790 writer.println("Succeeded"); 791 } 792 793 private void injectRotary(String[] args, PrintWriter writer) { 794 int i = 1; // 0 is command itself 795 int display = InputHalService.DISPLAY_MAIN; 796 int inputType = CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION; 797 boolean clockwise = false; 798 List<Long> deltaTimeMs = new ArrayList<>(); 799 try { 800 while (i < args.length) { 801 switch (args[i]) { 802 case "-d": 803 i++; 804 display = Integer.parseInt(args[i]); 805 break; 806 case "-i": 807 i++; 808 inputType = Integer.parseInt(args[i]); 809 break; 810 case "-c": 811 i++; 812 clockwise = Boolean.parseBoolean(args[i]); 813 break; 814 case "-dt": 815 i++; 816 while (i < args.length) { 817 deltaTimeMs.add(Long.parseLong(args[i])); 818 i++; 819 } 820 break; 821 default: 822 writer.println("Invalid option at index " + i + ": " + args[i]); 823 return; 824 } 825 i++; 826 } 827 } catch (Exception e) { 828 writer.println("Invalid args:" + e); 829 showHelp(writer); 830 return; 831 } 832 if (deltaTimeMs.isEmpty()) { 833 deltaTimeMs.add(0L); 834 } 835 for (int j = 0; j < deltaTimeMs.size(); j++) { 836 if (deltaTimeMs.get(j) < 0) { 837 writer.println("Delta time shouldn't be negative: " + deltaTimeMs.get(j)); 838 showHelp(writer); 839 return; 840 } 841 if (j > 0 && deltaTimeMs.get(j) > deltaTimeMs.get(j - 1)) { 842 writer.println("Delta times should be in descending order"); 843 showHelp(writer); 844 return; 845 } 846 } 847 long[] uptimeMs = new long[deltaTimeMs.size()]; 848 long currentUptime = SystemClock.uptimeMillis(); 849 for (int j = 0; j < deltaTimeMs.size(); j++) { 850 uptimeMs[j] = currentUptime - deltaTimeMs.get(j); 851 } 852 RotaryEvent rotaryEvent = new RotaryEvent(inputType, clockwise, uptimeMs); 853 mCarInputService.onRotaryEvent(rotaryEvent, display); 854 writer.println("Succeeded in injecting: " + rotaryEvent); 855 } 856 857 private void getInitialUserInfo(String[] args, PrintWriter writer) { 858 if (args.length < 2) { 859 writer.println("Insufficient number of args"); 860 return; 861 } 862 863 // Gets the request type 864 String typeArg = args[1]; 865 int requestType = UserHalHelper.parseInitialUserInfoRequestType(typeArg); 866 boolean halOnly = false; 867 868 int timeout = DEFAULT_HAL_TIMEOUT_MS; 869 for (int i = 2; i < args.length; i++) { 870 String arg = args[i]; 871 switch (arg) { 872 case "--timeout": 873 timeout = Integer.parseInt(args[++i]); 874 break; 875 case "--hal-only": 876 halOnly = true; 877 break; 878 default: 879 writer.println("Invalid option at index " + i + ": " + arg); 880 return; 881 882 } 883 } 884 885 Log.d(TAG, "handleGetInitialUserInfo(): type=" + requestType + " (" + typeArg 886 + "), timeout=" + timeout); 887 888 CountDownLatch latch = new CountDownLatch(1); 889 HalCallback<InitialUserInfoResponse> callback = (status, resp) -> { 890 try { 891 Log.d(TAG, "GetUserInfoResponse: status=" + status + ", resp=" + resp); 892 writer.printf("Call status: %s\n", 893 UserHalHelper.halCallbackStatusToString(status)); 894 if (status != HalCallback.STATUS_OK) { 895 return; 896 } 897 writer.printf("Request id: %d\n", resp.requestId); 898 writer.printf("Action: %s\n", 899 InitialUserInfoResponseAction.toString(resp.action)); 900 if (!TextUtils.isEmpty(resp.userNameToCreate)) { 901 writer.printf("User name: %s\n", resp.userNameToCreate); 902 } 903 if (resp.userToSwitchOrCreate.userId != UserHandle.USER_NULL) { 904 writer.printf("User id: %d\n", resp.userToSwitchOrCreate.userId); 905 } 906 if (resp.userToSwitchOrCreate.flags != UserFlags.NONE) { 907 writer.printf("User flags: %s\n", 908 UserHalHelper.userFlagsToString(resp.userToSwitchOrCreate.flags)); 909 } 910 if (!TextUtils.isEmpty(resp.userLocales)) { 911 writer.printf("User locales: %s\n", resp.userLocales); 912 } 913 } finally { 914 latch.countDown(); 915 } 916 }; 917 if (halOnly) { 918 UsersInfo usersInfo = generateUsersInfo(); 919 mHal.getUserHal().getInitialUserInfo(requestType, timeout, usersInfo, callback); 920 } else { 921 mCarUserService.getInitialUserInfo(requestType, callback); 922 } 923 waitForHal(writer, latch, timeout); 924 } 925 926 private UsersInfo generateUsersInfo() { 927 return UserHalHelper.newUsersInfo(UserManager.get(mContext)); 928 } 929 930 private int getUserHalFlags(@UserIdInt int userId) { 931 return UserHalHelper.getFlags(UserManager.get(mContext), userId); 932 } 933 934 private static void waitForHal(PrintWriter writer, CountDownLatch latch, int timeoutMs) { 935 try { 936 if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) { 937 writer.printf("HAL didn't respond in %dms\n", timeoutMs); 938 } 939 } catch (InterruptedException e) { 940 Thread.currentThread().interrupt(); 941 writer.println("Interrupted waiting for HAL"); 942 } 943 return; 944 } 945 946 private void switchUser(String[] args, PrintWriter writer) { 947 if (args.length < 2) { 948 writer.println("Insufficient number of args"); 949 return; 950 } 951 952 int targetUserId = Integer.parseInt(args[1]); 953 int timeout = DEFAULT_HAL_TIMEOUT_MS; 954 boolean halOnly = false; 955 956 for (int i = 2; i < args.length; i++) { 957 String arg = args[i]; 958 switch (arg) { 959 case "--timeout": 960 timeout = Integer.parseInt(args[++i]); 961 break; 962 case "--hal-only": 963 halOnly = true; 964 break; 965 default: 966 writer.println("Invalid option at index " + i + ": " + arg); 967 return; 968 } 969 } 970 971 Log.d(TAG, "switchUser(): target=" + targetUserId + ", halOnly=" + halOnly 972 + ", timeout=" + timeout); 973 974 if (halOnly) { 975 CountDownLatch latch = new CountDownLatch(1); 976 UserHalService userHal = mHal.getUserHal(); 977 UserInfo targetUserInfo = new UserInfo(); 978 targetUserInfo.userId = targetUserId; 979 targetUserInfo.flags = getUserHalFlags(targetUserId); 980 981 SwitchUserRequest request = new SwitchUserRequest(); 982 request.targetUser = targetUserInfo; 983 request.usersInfo = generateUsersInfo(); 984 985 userHal.switchUser(request, timeout, (status, resp) -> { 986 try { 987 Log.d(TAG, "SwitchUserResponse: status=" + status + ", resp=" + resp); 988 writer.printf("Call Status: %s\n", 989 UserHalHelper.halCallbackStatusToString(status)); 990 if (status != HalCallback.STATUS_OK) { 991 return; 992 } 993 writer.printf("Request id: %d\n", resp.requestId); 994 writer.printf("Message type: %s\n", 995 SwitchUserMessageType.toString(resp.messageType)); 996 writer.printf("Switch Status: %s\n", SwitchUserStatus.toString(resp.status)); 997 String errorMessage = resp.errorMessage; 998 if (!TextUtils.isEmpty(errorMessage)) { 999 writer.printf("Error message: %s", errorMessage); 1000 } 1001 // If HAL returned OK, make a "post-switch" call to the HAL indicating an 1002 // Android error. This is to "rollback" the HAL switch. 1003 if (status == HalCallback.STATUS_OK 1004 && resp.status == SwitchUserStatus.SUCCESS) { 1005 userHal.postSwitchResponse(request); 1006 } 1007 } finally { 1008 latch.countDown(); 1009 } 1010 }); 1011 waitForHal(writer, latch, timeout); 1012 return; 1013 } 1014 CarUserManager carUserManager = getCarUserManager(mContext); 1015 AndroidFuture<UserSwitchResult> future = carUserManager.switchUser(targetUserId); 1016 UserSwitchResult result = waitForFuture(writer, future, timeout); 1017 if (result == null) return; 1018 writer.printf("UserSwitchResult: status=%s", 1019 UserSwitchResult.statusToString(result.getStatus())); 1020 String msg = result.getErrorMessage(); 1021 if (!TextUtils.isEmpty(msg)) { 1022 writer.printf(", errorMessage=%s", msg); 1023 } 1024 writer.println(); 1025 } 1026 1027 private void createUser(String[] args, PrintWriter writer) { 1028 int timeout = DEFAULT_HAL_TIMEOUT_MS; 1029 int flags = 0; 1030 boolean halOnly = false; 1031 String name = null; 1032 String userType = null; 1033 1034 for (int i = 1; i < args.length; i++) { 1035 String arg = args[i]; 1036 switch (arg) { 1037 case "--timeout": 1038 timeout = Integer.parseInt(args[++i]); 1039 break; 1040 case "--hal-only": 1041 halOnly = true; 1042 break; 1043 case "--flags": 1044 flags = Integer.parseInt(args[++i]); 1045 break; 1046 case "--type": 1047 userType = args[++i]; 1048 break; 1049 default: 1050 if (name != null) { 1051 writer.println("Invalid option at index " + i + ": " + arg); 1052 return; 1053 } 1054 name = arg; 1055 } 1056 } 1057 1058 if (userType == null) { 1059 userType = android.content.pm.UserInfo.getDefaultUserType(flags); 1060 } 1061 1062 Log.d(TAG, "createUser(): name=" + name + ", userType=" + userType 1063 + ", flags=" + UserHalHelper.userFlagsToString(flags) 1064 + ", halOnly=" + halOnly + ", timeout=" + timeout); 1065 1066 if (!halOnly) { 1067 CarUserManager carUserManager = getCarUserManager(mContext); 1068 AndroidFuture<UserCreationResult> future = carUserManager 1069 .createUser(name, userType, flags); 1070 1071 UserCreationResult result = waitForFuture(writer, future, timeout); 1072 if (result == null) return; 1073 1074 android.content.pm.UserInfo user = result.getUser(); 1075 writer.printf("UserCreationResult: status=%s, user=%s", 1076 UserCreationResult.statusToString(result.getStatus()), 1077 user == null ? "N/A" : user.toFullString()); 1078 String msg = result.getErrorMessage(); 1079 if (!TextUtils.isEmpty(msg)) { 1080 writer.printf(", errorMessage=%s", msg); 1081 } 1082 writer.println(); 1083 return; 1084 } 1085 1086 CountDownLatch latch = new CountDownLatch(1); 1087 UserHalService userHal = mHal.getUserHal(); 1088 1089 CreateUserRequest request = new CreateUserRequest(); 1090 1091 UserManager um = UserManager.get(mContext); 1092 android.content.pm.UserInfo newUser = um.createUser(name, userType, flags); 1093 if (newUser == null) { 1094 writer.printf("Failed to create user"); 1095 return; 1096 } 1097 writer.printf("New user: %s\n", newUser.toFullString()); 1098 Log.i(TAG, "Created new user: " + newUser.toFullString()); 1099 1100 request.newUserInfo.userId = newUser.id; 1101 request.newUserInfo.flags = UserHalHelper.convertFlags(newUser); 1102 1103 request.usersInfo = generateUsersInfo(); 1104 1105 AtomicBoolean halOk = new AtomicBoolean(false); 1106 try { 1107 userHal.createUser(request, timeout, (status, resp) -> { 1108 Log.d(TAG, "CreateUserResponse: status=" + status + ", resp=" + resp); 1109 writer.printf("Call Status: %s\n", 1110 UserHalHelper.halCallbackStatusToString(status)); 1111 if (status == HalCallback.STATUS_OK) { 1112 halOk.set(resp.status == CreateUserStatus.SUCCESS); 1113 writer.printf("Request id: %d\n", resp.requestId); 1114 writer.printf("Create Status: %s\n", CreateUserStatus.toString(resp.status)); 1115 String errorMessage = resp.errorMessage; 1116 if (!TextUtils.isEmpty(errorMessage)) { 1117 writer.printf("Error message: %s", errorMessage); 1118 } 1119 } 1120 latch.countDown(); 1121 }); 1122 waitForHal(writer, latch, timeout); 1123 } catch (Exception e) { 1124 writer.printf("HAL failed: %s\n", e); 1125 } finally { 1126 if (!halOk.get()) { 1127 writer.printf("Removing user %d due to HAL failure\n", newUser.id); 1128 boolean removed = um.removeUser(newUser.id); 1129 writer.printf("User removed: %b\n", removed); 1130 } 1131 } 1132 } 1133 1134 private void removeUser(String[] args, PrintWriter writer) { 1135 if (args.length < 2) { 1136 writer.println("Insufficient number of args"); 1137 return; 1138 } 1139 1140 int userId = Integer.parseInt(args[1]); 1141 boolean halOnly = false; 1142 1143 for (int i = 2; i < args.length; i++) { 1144 String arg = args[i]; 1145 switch (arg) { 1146 case "--hal-only": 1147 halOnly = true; 1148 break; 1149 default: 1150 writer.println("Invalid option at index " + i + ": " + arg); 1151 return; 1152 } 1153 } 1154 1155 Log.d(TAG, "handleRemoveUser(): User to remove=" + userId + ", halOnly=" + halOnly); 1156 1157 if (halOnly) { 1158 UserHalService userHal = mHal.getUserHal(); 1159 UsersInfo usersInfo = generateUsersInfo(); 1160 UserInfo userInfo = new UserInfo(); 1161 userInfo.userId = userId; 1162 userInfo.flags = getUserHalFlags(userId); 1163 1164 RemoveUserRequest request = new RemoveUserRequest(); 1165 request.removedUserInfo = userInfo; 1166 request.usersInfo = usersInfo; 1167 1168 userHal.removeUser(request); 1169 writer.printf("User removal sent for HAL only.\n"); 1170 return; 1171 } 1172 1173 CarUserManager carUserManager = getCarUserManager(mContext); 1174 UserRemovalResult result = carUserManager.removeUser(userId); 1175 if (result == null) return; 1176 writer.printf("UserRemovalResult: status = %s\n", 1177 UserRemovalResult.statusToString(result.getStatus())); 1178 } 1179 1180 private static <T> T waitForFuture(@NonNull PrintWriter writer, 1181 @NonNull AndroidFuture<T> future, int timeoutMs) { 1182 T result = null; 1183 try { 1184 result = future.get(timeoutMs, TimeUnit.MILLISECONDS); 1185 if (result == null) { 1186 writer.printf("Service didn't respond in %d ms", timeoutMs); 1187 } 1188 } catch (Exception e) { 1189 writer.printf("Exception getting future: %s", e); 1190 } 1191 return result; 1192 } 1193 1194 private void getInitialUser(PrintWriter writer) { 1195 android.content.pm.UserInfo user = mCarUserService.getInitialUser(); 1196 writer.println(user == null ? NO_INITIAL_USER : user.id); 1197 } 1198 1199 private void getUserAuthAssociation(String[] args, PrintWriter writer) { 1200 if (args.length < 2) { 1201 writer.println("invalid usage, must pass at least 1 argument"); 1202 return; 1203 } 1204 1205 boolean halOnly = false; 1206 int userId = UserHandle.USER_CURRENT; 1207 1208 UserIdentificationGetRequest request = new UserIdentificationGetRequest(); 1209 for (int i = 1; i < args.length; i++) { 1210 String arg = args[i]; 1211 switch (arg) { 1212 case "--user": 1213 try { 1214 userId = Integer.parseInt(args[++i]); 1215 } catch (Exception e) { 1216 writer.printf("Invalid user id at index %d (from %s): %s\n", i + 1, 1217 Arrays.toString(args), arg); 1218 } 1219 break; 1220 case "--hal-only": 1221 halOnly = true; 1222 break; 1223 default: 1224 int type = parseAuthArg(VALID_USER_AUTH_TYPES, arg); 1225 if (type == INVALID_USER_AUTH_TYPE_OR_VALUE) { 1226 writer.printf("Invalid type at index %d (from %s): %s. %s\n", i + 1, 1227 Arrays.toString(args), arg, VALID_USER_AUTH_TYPES_HELP); 1228 return; 1229 } 1230 request.associationTypes.add(type); 1231 } 1232 1233 } 1234 if (userId == UserHandle.USER_CURRENT) { 1235 userId = ActivityManager.getCurrentUser(); 1236 } 1237 int requestSize = request.associationTypes.size(); 1238 if (halOnly) { 1239 request.numberAssociationTypes = requestSize; 1240 request.userInfo.userId = userId; 1241 request.userInfo.flags = getUserHalFlags(userId); 1242 1243 Log.d(TAG, "getUserAuthAssociation(): user=" + userId + ", halOnly=" + halOnly 1244 + ", request=" + request); 1245 UserIdentificationResponse response = mHal.getUserHal().getUserAssociation(request); 1246 Log.d(TAG, "getUserAuthAssociation(): response=" + response); 1247 showResponse(writer, response); 1248 return; 1249 } 1250 1251 CarUserManager carUserManager = getCarUserManager(writer, userId); 1252 int[] types = new int[requestSize]; 1253 for (int i = 0; i < requestSize; i++) { 1254 types[i] = request.associationTypes.get(i); 1255 } 1256 UserIdentificationAssociationResponse response = carUserManager 1257 .getUserIdentificationAssociation(types); 1258 showResponse(writer, response); 1259 } 1260 1261 private CarUserManager getCarUserManager(@NonNull PrintWriter writer, @UserIdInt int userId) { 1262 Context context; 1263 if (userId == mContext.getUserId()) { 1264 context = mContext; 1265 } else { 1266 context = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0); 1267 } 1268 int actualUserId = Binder.getCallingUid(); 1269 if (actualUserId != userId) { 1270 writer.printf("Emulating call for user id %d, but caller's user id is %d, so that's " 1271 + "what CarUserService will use when calling HAL.\n", userId, actualUserId); 1272 } 1273 1274 return getCarUserManager(context); 1275 } 1276 1277 private CarUserManager getCarUserManager(@NonNull Context context) { 1278 Car car = Car.createCar(context); 1279 CarUserManager carUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE); 1280 return carUserManager; 1281 } 1282 1283 private void showResponse(@NonNull PrintWriter writer, 1284 @NonNull UserIdentificationResponse response) { 1285 if (response == null) { 1286 writer.println("null response"); 1287 return; 1288 } 1289 1290 if (!TextUtils.isEmpty(response.errorMessage)) { 1291 writer.printf("Error message: %s\n", response.errorMessage); 1292 } 1293 int numberAssociations = response.associations.size(); 1294 writer.printf("%d associations:\n", numberAssociations); 1295 for (int i = 0; i < numberAssociations; i++) { 1296 UserIdentificationAssociation association = response.associations.get(i); 1297 writer.printf(" %s\n", association); 1298 } 1299 } 1300 1301 private void showResponse(@NonNull PrintWriter writer, 1302 @NonNull UserIdentificationAssociationResponse response) { 1303 if (response == null) { 1304 writer.println("null response"); 1305 return; 1306 } 1307 if (!response.isSuccess()) { 1308 writer.printf("failed response: %s\n", response); 1309 return; 1310 } 1311 String errorMessage = response.getErrorMessage(); 1312 if (!TextUtils.isEmpty(errorMessage)) { 1313 writer.printf("Error message: %s\n", errorMessage); 1314 } 1315 int[] values = response.getValues(); 1316 if (values == null) { 1317 writer.printf("no associations on %s\n", response); 1318 return; 1319 } 1320 writer.printf("%d associations:\n", values.length); 1321 for (int i = 0; i < values.length; i++) { 1322 writer.printf(" %s\n", UserIdentificationAssociationValue.toString(values[i])); 1323 } 1324 } 1325 1326 private void setUserAuthAssociation(String[] args, PrintWriter writer) { 1327 if (args.length < 3) { 1328 writer.println("invalid usage, must pass at least 4 arguments"); 1329 return; 1330 } 1331 1332 boolean halOnly = false; 1333 int timeout = DEFAULT_HAL_TIMEOUT_MS; 1334 int userId = UserHandle.USER_CURRENT; 1335 1336 UserIdentificationSetRequest request = new UserIdentificationSetRequest(); 1337 for (int i = 1; i < args.length; i++) { 1338 String arg = args[i]; 1339 switch (arg) { 1340 case "--user": 1341 try { 1342 userId = Integer.parseInt(args[++i]); 1343 } catch (Exception e) { 1344 writer.printf("Invalid user id at index %d (from %s): %s\n", i + 1, 1345 Arrays.toString(args), arg); 1346 } 1347 break; 1348 case "--hal-only": 1349 halOnly = true; 1350 break; 1351 case "--timeout": 1352 timeout = Integer.parseInt(args[++i]); 1353 break; 1354 default: 1355 UserIdentificationSetAssociation association = 1356 new UserIdentificationSetAssociation(); 1357 association.type = parseAuthArg(VALID_USER_AUTH_TYPES, arg); 1358 if (association.type == INVALID_USER_AUTH_TYPE_OR_VALUE) { 1359 writer.printf("Invalid type at index %d (from %s): %s. %s\n", i + 1, 1360 Arrays.toString(args), arg, VALID_USER_AUTH_TYPES_HELP); 1361 return; 1362 } 1363 association.value = parseAuthArg(VALID_USER_AUTH_SET_VALUES, args[++i]); 1364 if (association.value == INVALID_USER_AUTH_TYPE_OR_VALUE) { 1365 writer.printf("Invalid value at index %d (from %s): %s. %s\n", i + 1, 1366 Arrays.toString(args), arg, VALID_USER_AUTH_SET_VALUES_HELP); 1367 return; 1368 } 1369 request.associations.add(association); 1370 } 1371 1372 } 1373 if (userId == UserHandle.USER_CURRENT) { 1374 userId = ActivityManager.getCurrentUser(); 1375 } 1376 int requestSize = request.associations.size(); 1377 if (halOnly) { 1378 request.numberAssociations = requestSize; 1379 request.userInfo.userId = userId; 1380 request.userInfo.flags = getUserHalFlags(userId); 1381 1382 Log.d(TAG, "setUserAuthAssociation(): user=" + userId + ", halOnly=" + halOnly 1383 + ", request=" + request); 1384 CountDownLatch latch = new CountDownLatch(1); 1385 mHal.getUserHal().setUserAssociation(timeout, request, (status, response) -> { 1386 Log.d(TAG, "setUserAuthAssociation(): response=" + response); 1387 try { 1388 showResponse(writer, response); 1389 } finally { 1390 latch.countDown(); 1391 } 1392 }); 1393 waitForHal(writer, latch, timeout); 1394 return; 1395 } 1396 CarUserManager carUserManager = getCarUserManager(writer, userId); 1397 int[] types = new int[requestSize]; 1398 int[] values = new int[requestSize]; 1399 for (int i = 0; i < requestSize; i++) { 1400 UserIdentificationSetAssociation association = request.associations.get(i); 1401 types[i] = association.type; 1402 values[i] = association.value; 1403 } 1404 AndroidFuture<UserIdentificationAssociationResponse> future = carUserManager 1405 .setUserIdentificationAssociation(types, values); 1406 UserIdentificationAssociationResponse response = waitForFuture(writer, future, timeout); 1407 if (response != null) { 1408 showResponse(writer, response); 1409 } 1410 } 1411 1412 private static int parseAuthArg(@NonNull SparseArray<String> types, @NonNull String type) { 1413 for (int i = 0; i < types.size(); i++) { 1414 if (types.valueAt(i).equals(type)) { 1415 return types.keyAt(i); 1416 } 1417 } 1418 return INVALID_USER_AUTH_TYPE_OR_VALUE; 1419 } 1420 1421 private void forceDayNightMode(String arg, PrintWriter writer) { 1422 int mode; 1423 switch (arg) { 1424 case PARAM_DAY_MODE: 1425 mode = CarNightService.FORCED_DAY_MODE; 1426 break; 1427 case PARAM_NIGHT_MODE: 1428 mode = CarNightService.FORCED_NIGHT_MODE; 1429 break; 1430 case PARAM_SENSOR_MODE: 1431 mode = CarNightService.FORCED_SENSOR_MODE; 1432 break; 1433 default: 1434 writer.println("Unknown value. Valid argument: " + PARAM_DAY_MODE + "|" 1435 + PARAM_NIGHT_MODE + "|" + PARAM_SENSOR_MODE); 1436 return; 1437 } 1438 int current = mCarNightService.forceDayNightMode(mode); 1439 String currentMode = null; 1440 switch (current) { 1441 case UiModeManager.MODE_NIGHT_AUTO: 1442 currentMode = PARAM_SENSOR_MODE; 1443 break; 1444 case UiModeManager.MODE_NIGHT_YES: 1445 currentMode = PARAM_NIGHT_MODE; 1446 break; 1447 case UiModeManager.MODE_NIGHT_NO: 1448 currentMode = PARAM_DAY_MODE; 1449 break; 1450 } 1451 writer.println("DayNightMode changed to: " + currentMode); 1452 } 1453 1454 private void forceGarageMode(String arg, PrintWriter writer) { 1455 switch (arg) { 1456 case PARAM_ON_MODE: 1457 mSystemInterface.setDisplayState(false); 1458 mGarageModeService.forceStartGarageMode(); 1459 writer.println("Garage mode: " + mGarageModeService.isGarageModeActive()); 1460 break; 1461 case PARAM_OFF_MODE: 1462 mSystemInterface.setDisplayState(true); 1463 mGarageModeService.stopAndResetGarageMode(); 1464 writer.println("Garage mode: " + mGarageModeService.isGarageModeActive()); 1465 break; 1466 case PARAM_QUERY_MODE: 1467 mGarageModeService.dump(writer); 1468 break; 1469 case PARAM_REBOOT: 1470 mCarPowerManagementService.forceSuspendAndMaybeReboot(true); 1471 writer.println("Entering Garage Mode. Will reboot when it completes."); 1472 break; 1473 default: 1474 writer.println("Unknown value. Valid argument: " + PARAM_ON_MODE + "|" 1475 + PARAM_OFF_MODE + "|" + PARAM_QUERY_MODE + "|" + PARAM_REBOOT); 1476 } 1477 } 1478 1479 /** 1480 * Inject a fake VHAL event 1481 * 1482 * @param property the Vehicle property Id as defined in the HAL 1483 * @param zone Zone that this event services 1484 * @param isErrorEvent indicates the type of event 1485 * @param value Data value of the event 1486 * @param delayTime the event timestamp is increased by delayTime 1487 * @param writer PrintWriter 1488 */ 1489 private void injectVhalEvent(String property, String zone, String value, 1490 boolean isErrorEvent, String delayTime, PrintWriter writer) { 1491 if (zone != null && (zone.equalsIgnoreCase(PARAM_VEHICLE_PROPERTY_AREA_GLOBAL))) { 1492 if (!isPropertyAreaTypeGlobal(property)) { 1493 writer.println("Property area type inconsistent with given zone"); 1494 return; 1495 } 1496 } 1497 try { 1498 if (isErrorEvent) { 1499 mHal.injectOnPropertySetError(property, zone, value); 1500 } else { 1501 mHal.injectVhalEvent(property, zone, value, delayTime); 1502 } 1503 } catch (NumberFormatException e) { 1504 writer.println("Invalid property Id zone Id or value" + e); 1505 showHelp(writer); 1506 } 1507 } 1508 1509 // Check if the given property is global 1510 private static boolean isPropertyAreaTypeGlobal(@Nullable String property) { 1511 if (property == null) { 1512 return false; 1513 } 1514 return (Integer.decode(property) & VehicleArea.MASK) == VehicleArea.GLOBAL; 1515 } 1516 } 1517