/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.car; import android.annotation.MainThread; import android.app.UiModeManager; import android.car.Car; import android.car.ICar; import android.car.cluster.renderer.IInstrumentClusterNavigation; import android.car.user.CarUserManagerHelper; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.automotive.vehicle.V2_0.IVehicle; import android.hardware.automotive.vehicle.V2_0.VehicleArea; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.Process; import android.os.Trace; import android.util.Log; import android.util.Slog; import android.util.TimingsTraceLog; import com.android.car.cluster.InstrumentClusterService; import com.android.car.hal.VehicleHal; import com.android.car.internal.FeatureConfiguration; import com.android.car.pm.CarPackageManagerService; import com.android.car.systeminterface.SystemInterface; import com.android.car.user.CarUserService; import com.android.internal.annotations.GuardedBy; import com.android.internal.car.ICarServiceHelper; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ICarImpl extends ICar.Stub { public static final String INTERNAL_INPUT_SERVICE = "internal_input"; public static final String INTERNAL_SYSTEM_ACTIVITY_MONITORING_SERVICE = "system_activity_monitoring"; private final Context mContext; private final VehicleHal mHal; private final SystemInterface mSystemInterface; private final SystemActivityMonitoringService mSystemActivityMonitoringService; private final CarPowerManagementService mCarPowerManagementService; private final CarPackageManagerService mCarPackageManagerService; private final CarInputService mCarInputService; private final CarDrivingStateService mCarDrivingStateService; private final CarUxRestrictionsManagerService mCarUXRestrictionsService; private final CarAudioService mCarAudioService; private final CarProjectionService mCarProjectionService; private final CarPropertyService mCarPropertyService; private final CarNightService mCarNightService; private final AppFocusService mAppFocusService; private final GarageModeService mGarageModeService; private final InstrumentClusterService mInstrumentClusterService; private final CarLocationService mCarLocationService; private final SystemStateControllerService mSystemStateControllerService; private final CarBluetoothService mCarBluetoothService; private final PerUserCarServiceHelper mPerUserCarServiceHelper; private final CarDiagnosticService mCarDiagnosticService; private final CarStorageMonitoringService mCarStorageMonitoringService; private final CarConfigurationService mCarConfigurationService; private final CarUserManagerHelper mUserManagerHelper; private CarUserService mCarUserService; private VmsSubscriberService mVmsSubscriberService; private VmsPublisherService mVmsPublisherService; private final CarServiceBase[] mAllServices; private static final String TAG = "ICarImpl"; private static final String VHAL_TIMING_TAG = "VehicleHalTiming"; private static final TimingsTraceLog mBootTiming = new TimingsTraceLog(VHAL_TIMING_TAG, Trace.TRACE_TAG_HAL); /** Test only service. Populate it only when necessary. */ @GuardedBy("this") private CarTestService mCarTestService; @GuardedBy("this") private ICarServiceHelper mICarServiceHelper; private final String mVehicleInterfaceName; public ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface, CanBusErrorNotifier errorNotifier, String vehicleInterfaceName) { mContext = serviceContext; mSystemInterface = systemInterface; mHal = new VehicleHal(vehicle); mVehicleInterfaceName = vehicleInterfaceName; mSystemActivityMonitoringService = new SystemActivityMonitoringService(serviceContext); mCarPowerManagementService = new CarPowerManagementService(mContext, mHal.getPowerHal(), systemInterface); mCarPropertyService = new CarPropertyService(serviceContext, mHal.getPropertyHal()); mCarDrivingStateService = new CarDrivingStateService(serviceContext, mCarPropertyService); mCarUXRestrictionsService = new CarUxRestrictionsManagerService(serviceContext, mCarDrivingStateService, mCarPropertyService); mCarPackageManagerService = new CarPackageManagerService(serviceContext, mCarUXRestrictionsService, mSystemActivityMonitoringService); mCarInputService = new CarInputService(serviceContext, mHal.getInputHal()); mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService); mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService); mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService, mCarPropertyService); mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService); mCarAudioService = new CarAudioService(serviceContext); mCarNightService = new CarNightService(serviceContext, mCarPropertyService); mInstrumentClusterService = new InstrumentClusterService(serviceContext, mAppFocusService, mCarInputService); mSystemStateControllerService = new SystemStateControllerService(serviceContext, mCarPowerManagementService, mCarAudioService, this); mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext); mCarBluetoothService = new CarBluetoothService(serviceContext, mCarPropertyService, mPerUserCarServiceHelper, mCarUXRestrictionsService); mVmsSubscriberService = new VmsSubscriberService(serviceContext, mHal.getVmsHal()); mVmsPublisherService = new VmsPublisherService(serviceContext, mHal.getVmsHal()); mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal()); mCarStorageMonitoringService = new CarStorageMonitoringService(serviceContext, systemInterface); mCarConfigurationService = new CarConfigurationService(serviceContext, new JsonReaderImpl()); mUserManagerHelper = new CarUserManagerHelper(serviceContext); // Be careful with order. Service depending on other service should be inited later. List allServices = new ArrayList<>(); allServices.add(mSystemActivityMonitoringService); allServices.add(mCarPowerManagementService); allServices.add(mCarPropertyService); allServices.add(mCarDrivingStateService); allServices.add(mCarUXRestrictionsService); allServices.add(mCarPackageManagerService); allServices.add(mCarInputService); allServices.add(mCarLocationService); allServices.add(mGarageModeService); allServices.add(mAppFocusService); allServices.add(mCarAudioService); allServices.add(mCarNightService); allServices.add(mInstrumentClusterService); allServices.add(mCarProjectionService); allServices.add(mSystemStateControllerService); allServices.add(mCarBluetoothService); allServices.add(mCarDiagnosticService); allServices.add(mPerUserCarServiceHelper); allServices.add(mCarStorageMonitoringService); allServices.add(mCarConfigurationService); allServices.add(mVmsSubscriberService); allServices.add(mVmsPublisherService); if (mUserManagerHelper.isHeadlessSystemUser()) { mCarUserService = new CarUserService(serviceContext, mUserManagerHelper); allServices.add(mCarUserService); } mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]); } @MainThread void init() { traceBegin("VehicleHal.init"); mHal.init(); traceEnd(); traceBegin("CarService.initAllServices"); for (CarServiceBase service : mAllServices) { service.init(); } traceEnd(); } void release() { // release done in opposite order from init for (int i = mAllServices.length - 1; i >= 0; i--) { mAllServices[i].release(); } mHal.release(); } void vehicleHalReconnected(IVehicle vehicle) { mHal.vehicleHalReconnected(vehicle); for (CarServiceBase service : mAllServices) { service.vehicleHalReconnected(); } } @Override public void setCarServiceHelper(IBinder helper) { int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID) { throw new SecurityException("Only allowed from system"); } synchronized (this) { mICarServiceHelper = ICarServiceHelper.Stub.asInterface(helper); mSystemInterface.setCarServiceHelper(mICarServiceHelper); } } @Override public IBinder getCarService(String serviceName) { switch (serviceName) { case Car.AUDIO_SERVICE: return mCarAudioService; case Car.APP_FOCUS_SERVICE: return mAppFocusService; case Car.PACKAGE_SERVICE: return mCarPackageManagerService; case Car.DIAGNOSTIC_SERVICE: assertAnyDiagnosticPermission(mContext); return mCarDiagnosticService; case Car.POWER_SERVICE: assertPowerPermission(mContext); return mCarPowerManagementService; case Car.CABIN_SERVICE: case Car.HVAC_SERVICE: case Car.INFO_SERVICE: case Car.PROPERTY_SERVICE: case Car.SENSOR_SERVICE: case Car.VENDOR_EXTENSION_SERVICE: return mCarPropertyService; case Car.CAR_NAVIGATION_SERVICE: assertNavigationManagerPermission(mContext); IInstrumentClusterNavigation navService = mInstrumentClusterService.getNavigationService(); return navService == null ? null : navService.asBinder(); case Car.CAR_INSTRUMENT_CLUSTER_SERVICE: assertClusterManagerPermission(mContext); return mInstrumentClusterService.getManagerService(); case Car.PROJECTION_SERVICE: assertProjectionPermission(mContext); return mCarProjectionService; case Car.VMS_SUBSCRIBER_SERVICE: assertVmsSubscriberPermission(mContext); return mVmsSubscriberService; case Car.TEST_SERVICE: { assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE); synchronized (this) { if (mCarTestService == null) { mCarTestService = new CarTestService(mContext, this); } return mCarTestService; } } case Car.BLUETOOTH_SERVICE: return mCarBluetoothService; case Car.STORAGE_MONITORING_SERVICE: assertPermission(mContext, Car.PERMISSION_STORAGE_MONITORING); return mCarStorageMonitoringService; case Car.CAR_DRIVING_STATE_SERVICE: assertDrivingStatePermission(mContext); return mCarDrivingStateService; case Car.CAR_UX_RESTRICTION_SERVICE: return mCarUXRestrictionsService; case Car.CAR_CONFIGURATION_SERVICE: return mCarConfigurationService; default: Log.w(CarLog.TAG_SERVICE, "getCarService for unknown service:" + serviceName); return null; } } @Override public int getCarConnectionType() { return Car.CONNECTION_TYPE_EMBEDDED; } public CarServiceBase getCarInternalService(String serviceName) { switch (serviceName) { case INTERNAL_INPUT_SERVICE: return mCarInputService; case INTERNAL_SYSTEM_ACTIVITY_MONITORING_SERVICE: return mSystemActivityMonitoringService; default: Log.w(CarLog.TAG_SERVICE, "getCarInternalService for unknown service:" + serviceName); return null; } } public static void assertVehicleHalMockPermission(Context context) { assertPermission(context, Car.PERMISSION_MOCK_VEHICLE_HAL); } public static void assertNavigationManagerPermission(Context context) { assertPermission(context, Car.PERMISSION_CAR_NAVIGATION_MANAGER); } public static void assertClusterManagerPermission(Context context) { assertPermission(context, Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL); } public static void assertPowerPermission(Context context) { assertPermission(context, Car.PERMISSION_CAR_POWER); } public static void assertProjectionPermission(Context context) { assertPermission(context, Car.PERMISSION_CAR_PROJECTION); } public static void assertAnyDiagnosticPermission(Context context) { assertAnyPermission(context, Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL, Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR); } public static void assertDrivingStatePermission(Context context) { assertPermission(context, Car.PERMISSION_CAR_DRIVING_STATE); } public static void assertVmsPublisherPermission(Context context) { assertPermission(context, Car.PERMISSION_VMS_PUBLISHER); } public static void assertVmsSubscriberPermission(Context context) { assertPermission(context, Car.PERMISSION_VMS_SUBSCRIBER); } public static void assertPermission(Context context, String permission) { if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("requires " + permission); } } /** * Checks to see if the caller has a permission. * @param context * @param permission * * @return boolean TRUE if caller has the permission. */ public static boolean hasPermission(Context context, String permission) { return context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; } public static void assertAnyPermission(Context context, String... permissions) { for (String permission : permissions) { if (context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) { return; } } throw new SecurityException("requires any of " + Arrays.toString(permissions)); } @Override protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { writer.println("Permission Denial: can't dump CarService from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " without permission " + android.Manifest.permission.DUMP); return; } if (args == null || args.length == 0) { writer.println("*dump car service*"); writer.println("*FutureConfig, DEFAULT:" + FeatureConfiguration.DEFAULT); writer.println("*Dump all services*"); for (CarServiceBase service : mAllServices) { dumpService(service, writer); } if (mCarTestService != null) { dumpService(mCarTestService, writer); } writer.println("*Dump Vehicle HAL*"); writer.println("Vehicle HAL Interface: " + mVehicleInterfaceName); try { // TODO dump all feature flags by creating a dumpable interface mHal.dump(writer); } catch (Exception e) { writer.println("Failed dumping: " + mHal.getClass().getName()); e.printStackTrace(writer); } } else if (Build.IS_USERDEBUG || Build.IS_ENG) { execShellCmd(args, writer); } else { writer.println("Commands not supported in " + Build.TYPE); } } private void dumpService(CarServiceBase service, PrintWriter writer) { try { service.dump(writer); } catch (Exception e) { writer.println("Failed dumping: " + service.getClass().getName()); e.printStackTrace(writer); } } void execShellCmd(String[] args, PrintWriter writer) { new CarShellCommand().exec(args, writer); } @MainThread private static void traceBegin(String name) { Slog.i(TAG, name); mBootTiming.traceBegin(name); } @MainThread private static void traceEnd() { mBootTiming.traceEnd(); } private class CarShellCommand { private static final String COMMAND_HELP = "-h"; private static final String COMMAND_DAY_NIGHT_MODE = "day-night-mode"; private static final String COMMAND_INJECT_VHAL_EVENT = "inject-vhal-event"; private static final String COMMAND_ENABLE_UXR = "enable-uxr"; private static final String COMMAND_GARAGE_MODE = "garage-mode"; private static final String COMMAND_GET_DO_ACTIVITIES = "get-do-activities"; private static final String PARAM_DAY_MODE = "day"; private static final String PARAM_NIGHT_MODE = "night"; private static final String PARAM_SENSOR_MODE = "sensor"; private static final String PARAM_VEHICLE_PROPERTY_AREA_GLOBAL = "0"; private static final String PARAM_ON_MODE = "on"; private static final String PARAM_OFF_MODE = "off"; private static final String PARAM_QUERY_MODE = "query"; private void dumpHelp(PrintWriter pw) { pw.println("Car service commands:"); pw.println("\t-h"); pw.println("\t Print this help text."); pw.println("\tday-night-mode [day|night|sensor]"); pw.println("\t Force into day/night mode or restore to auto."); pw.println("\tinject-vhal-event property [zone] data(can be comma separated list)"); pw.println("\t Inject a vehicle property for testing"); pw.println("\tdisable-uxr true|false"); pw.println("\t Disable UX restrictions and App blocking."); pw.println("\tgarage-mode [on|off|query]"); pw.println("\t Force into garage mode or check status."); pw.println("\tget-do-activities pkgname"); pw.println("\t Get Distraction Optimized activities in given package"); } public void exec(String[] args, PrintWriter writer) { String arg = args[0]; switch (arg) { case COMMAND_HELP: dumpHelp(writer); break; case COMMAND_DAY_NIGHT_MODE: { String value = args.length < 1 ? "" : args[1]; forceDayNightMode(value, writer); break; } case COMMAND_GARAGE_MODE: { String value = args.length < 1 ? "" : args[1]; forceGarageMode(value, writer); break; } case COMMAND_INJECT_VHAL_EVENT: String zone = PARAM_VEHICLE_PROPERTY_AREA_GLOBAL; String data; if (args.length < 3) { writer.println("Incorrect number of arguments."); dumpHelp(writer); break; } else if (args.length > 3) { // Zoned zone = args[2]; data = args[3]; } else { // Global data = args[2]; } injectVhalEvent(args[1], zone, data, writer); break; case COMMAND_ENABLE_UXR: if (args.length < 2) { writer.println("Incorrect number of arguments"); dumpHelp(writer); break; } boolean enableBlocking = Boolean.valueOf(args[1]); if (mCarPackageManagerService != null) { mCarPackageManagerService.setEnableActivityBlocking(enableBlocking); } break; case COMMAND_GET_DO_ACTIVITIES: if (args.length < 2) { writer.println("Incorrect number of arguments"); dumpHelp(writer); break; } String pkgName = args[1].toLowerCase(); if (mCarPackageManagerService != null) { String[] doActivities = mCarPackageManagerService.getDistractionOptimizedActivities( pkgName); if (doActivities != null) { writer.println("DO Activities for " + pkgName); for (String a : doActivities) { writer.println(a); } } else { writer.println("No DO Activities for " + pkgName); } } break; default: writer.println("Unknown command."); dumpHelp(writer); } } private void forceDayNightMode(String arg, PrintWriter writer) { int mode; switch (arg) { case PARAM_DAY_MODE: mode = CarNightService.FORCED_DAY_MODE; break; case PARAM_NIGHT_MODE: mode = CarNightService.FORCED_NIGHT_MODE; break; case PARAM_SENSOR_MODE: mode = CarNightService.FORCED_SENSOR_MODE; break; default: writer.println("Unknown value. Valid argument: " + PARAM_DAY_MODE + "|" + PARAM_NIGHT_MODE + "|" + PARAM_SENSOR_MODE); return; } int current = mCarNightService.forceDayNightMode(mode); String currentMode = null; switch (current) { case UiModeManager.MODE_NIGHT_AUTO: currentMode = PARAM_SENSOR_MODE; break; case UiModeManager.MODE_NIGHT_YES: currentMode = PARAM_NIGHT_MODE; break; case UiModeManager.MODE_NIGHT_NO: currentMode = PARAM_DAY_MODE; break; } writer.println("DayNightMode changed to: " + currentMode); } private void forceGarageMode(String arg, PrintWriter writer) { switch (arg) { case PARAM_ON_MODE: mGarageModeService.onPrepareShutdown(false); break; case PARAM_OFF_MODE: mGarageModeService.onSleepEntry(); break; case PARAM_QUERY_MODE: // Nothing to do. Always query at the end anyway. break; default: writer.println("Unknown value. Valid argument: " + PARAM_ON_MODE + "|" + PARAM_OFF_MODE + "|" + PARAM_QUERY_MODE); return; } writer.println("Garage mode: " + mGarageModeService.isInGarageMode()); } /** * Inject a fake VHAL event * * @param property the Vehicle property Id as defined in the HAL * @param zone Zone that this event services * @param value Data value of the event * @param writer PrintWriter */ private void injectVhalEvent(String property, String zone, String value, PrintWriter writer) { if (zone != null && (zone.equalsIgnoreCase(PARAM_VEHICLE_PROPERTY_AREA_GLOBAL))) { if (!isPropertyAreaTypeGlobal(property)) { writer.println("Property area type inconsistent with given zone"); return; } } try { mHal.injectVhalEvent(property, zone, value); } catch (NumberFormatException e) { writer.println("Invalid property Id zone Id or value" + e); dumpHelp(writer); } } // Check if the given property is global private boolean isPropertyAreaTypeGlobal(String property) { if (property == null) { return false; } return (Integer.decode(property) & VehicleArea.MASK) == VehicleArea.GLOBAL; } } }