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