blob: ebfa0e37e844883d2694a3d103bda1ee635fda3f [file] [log] [blame]
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -08001/*
2 * Copyright (C) 2018 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
17package com.android.car;
18
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070019import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_IDLING;
20import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING;
21import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
22import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
Yao, Yuxing8f475c12019-02-25 16:25:01 -080023import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
24
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080025import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
26
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -080027import android.annotation.IntDef;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070028import android.annotation.NonNull;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080029import android.annotation.Nullable;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080030import android.car.Car;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080031import android.car.drivingstate.CarDrivingStateEvent;
32import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
Ram Periathiruvadi4526a432018-01-24 13:00:54 -080033import android.car.drivingstate.CarUxRestrictions;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080034import android.car.drivingstate.CarUxRestrictionsConfiguration;
Yao, Yuxing8f475c12019-02-25 16:25:01 -080035import android.car.drivingstate.CarUxRestrictionsManager;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080036import android.car.drivingstate.ICarDrivingStateChangeListener;
Ram Periathiruvadi4526a432018-01-24 13:00:54 -080037import android.car.drivingstate.ICarUxRestrictionsChangeListener;
38import android.car.drivingstate.ICarUxRestrictionsManager;
Steve Paik4d257022018-04-27 13:28:31 -070039import android.car.hardware.CarPropertyValue;
40import android.car.hardware.property.CarPropertyEvent;
41import android.car.hardware.property.ICarPropertyEventListener;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080042import android.content.Context;
Ram Periathiruvadicdee1202018-07-17 18:01:31 -070043import android.content.pm.PackageManager;
Steve Paik4d257022018-04-27 13:28:31 -070044import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070045import android.hardware.display.DisplayManager;
Ram Periathiruvadicdee1202018-07-17 18:01:31 -070046import android.os.Binder;
47import android.os.Build;
Jordan Jozwiak576578a2020-02-27 17:06:24 -080048import android.os.Handler;
49import android.os.HandlerThread;
Yuncheol Heob9554e12019-10-08 15:51:49 -070050import android.os.IRemoteCallback;
Ram Periathiruvadicdee1202018-07-17 18:01:31 -070051import android.os.Process;
Yuncheol Heob9554e12019-10-08 15:51:49 -070052import android.os.RemoteCallbackList;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080053import android.os.RemoteException;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080054import android.os.SystemClock;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070055import android.util.ArraySet;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080056import android.util.AtomicFile;
Felipe Leme176a5fd2021-01-20 15:48:33 -080057import android.util.IndentingPrintWriter;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080058import android.util.JsonReader;
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -080059import android.util.JsonToken;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080060import android.util.JsonWriter;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080061import android.util.Log;
Yao, Yuxing9bfb7492019-02-15 11:53:34 -080062import android.util.Slog;
Yuncheol Heob9554e12019-10-08 15:51:49 -070063import android.util.SparseArray;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070064import android.view.Display;
65import android.view.DisplayAddress;
Ram Periathiruvadiec551ae2018-03-15 11:38:07 -070066
Yao, Yuxing9bfb7492019-02-15 11:53:34 -080067import com.android.car.systeminterface.SystemInterface;
Ram Periathiruvadicdee1202018-07-17 18:01:31 -070068import com.android.internal.annotations.GuardedBy;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080069import com.android.internal.annotations.VisibleForTesting;
Ram Periathiruvadicdee1202018-07-17 18:01:31 -070070
Ram Periathiruvadiec551ae2018-03-15 11:38:07 -070071import org.xmlpull.v1.XmlPullParserException;
72
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080073import java.io.File;
74import java.io.FileOutputStream;
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -080075import java.io.IOException;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080076import java.io.InputStreamReader;
77import java.io.OutputStreamWriter;
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -080078import java.lang.annotation.Retention;
79import java.lang.annotation.RetentionPolicy;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080080import java.nio.charset.StandardCharsets;
81import java.nio.file.Files;
82import java.nio.file.Path;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080083import java.util.ArrayList;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070084import java.util.HashMap;
Ram Periathiruvadi22c0d682018-05-10 17:50:48 -070085import java.util.LinkedList;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080086import java.util.List;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070087import java.util.Map;
Daulet Zhanguzin4ded6782020-01-03 15:47:10 +000088import java.util.Objects;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -070089import java.util.Set;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -080090
91/**
92 * A service that listens to current driving state of the vehicle and maps it to the
93 * appropriate UX restrictions for that driving state.
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080094 * <p>
95 * <h1>UX Restrictions Configuration</h1>
96 * When this service starts, it will first try reading the configuration set through
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -080097 * {@link #saveUxRestrictionsConfigurationForNextBoot(List)}.
Yao, Yuxing0811d5a2018-11-29 16:18:53 -080098 * If one is not available, it will try reading the configuration saved in
99 * {@code R.xml.car_ux_restrictions_map}. If XML is somehow unavailable, it will
100 * fall back to a hard-coded configuration.
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700101 * <p>
102 * <h1>Multi-Display</h1>
103 * Only physical displays that are available at service initialization are recognized.
104 * This service does not support pluggable displays.
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800105 */
Ram Periathiruvadi4526a432018-01-24 13:00:54 -0800106public class CarUxRestrictionsManagerService extends ICarUxRestrictionsManager.Stub implements
107 CarServiceBase {
Mayank Garg72c71d22021-02-03 23:54:45 -0800108 private static final String TAG = CarLog.tagFor(CarUxRestrictionsManagerService.class);
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800109 private static final boolean DBG = false;
Ram Periathiruvadi22c0d682018-05-10 17:50:48 -0700110 private static final int MAX_TRANSITION_LOG_SIZE = 20;
Steve Paik4d257022018-04-27 13:28:31 -0700111 private static final int PROPERTY_UPDATE_RATE = 5; // Update rate in Hz
Ram Periathiruvadif6381fb2018-06-05 18:44:19 -0700112 private static final float SPEED_NOT_AVAILABLE = -1.0F;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800113
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800114 private static final int UNKNOWN_JSON_SCHEMA_VERSION = -1;
115 private static final int JSON_SCHEMA_VERSION_V1 = 1;
116 private static final int JSON_SCHEMA_VERSION_V2 = 2;
117
118 @IntDef({UNKNOWN_JSON_SCHEMA_VERSION, JSON_SCHEMA_VERSION_V1, JSON_SCHEMA_VERSION_V2})
119 @Retention(RetentionPolicy.SOURCE)
120 private @interface JsonSchemaVersion {}
121
122 private static final String JSON_NAME_SCHEMA_VERSION = "schema_version";
123 private static final String JSON_NAME_RESTRICTIONS = "restrictions";
Marin Shalamanove85bd562020-04-24 17:21:32 +0200124 private static final int DEFAULT_PORT = 0;
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800125
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800126 @VisibleForTesting
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700127 static final String CONFIG_FILENAME_PRODUCTION = "ux_restrictions_prod_config.json";
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800128 @VisibleForTesting
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700129 static final String CONFIG_FILENAME_STAGED = "ux_restrictions_staged_config.json";
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800130
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800131 private final Context mContext;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700132 private final DisplayManager mDisplayManager;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800133 private final CarDrivingStateService mDrivingStateService;
Steve Paik4d257022018-04-27 13:28:31 -0700134 private final CarPropertyService mCarPropertyService;
Keun young Parkb241d022020-04-20 20:31:34 -0700135 private final HandlerThread mClientDispatchThread = CarServiceUtils.getHandlerThread(
136 getClass().getSimpleName());
137 private final Handler mClientDispatchHandler = new Handler(mClientDispatchThread.getLooper());
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800138 private final RemoteCallbackList<ICarUxRestrictionsChangeListener> mUxRClients =
139 new RemoteCallbackList<>();
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700140
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800141 /**
142 * Metadata associated with a binder callback.
143 */
144 private static class RemoteCallbackListCookie {
Marin Shalamanove85bd562020-04-24 17:21:32 +0200145 final Integer mPhysicalPort;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700146
Marin Shalamanove85bd562020-04-24 17:21:32 +0200147 RemoteCallbackListCookie(Integer physicalPort) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800148 mPhysicalPort = physicalPort;
149 }
150 }
151
152 private final Object mLock = new Object();
153
Jordan Jozwiak5465ca92020-02-03 12:04:12 -0800154 /**
Marin Shalamanove85bd562020-04-24 17:21:32 +0200155 * This lookup caches the mapping from an int display id to an int that represents a physical
Jordan Jozwiak5465ca92020-02-03 12:04:12 -0800156 * port. It includes mappings for virtual displays.
157 */
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800158 @GuardedBy("mLock")
Marin Shalamanove85bd562020-04-24 17:21:32 +0200159 private final Map<Integer, Integer> mPortLookup = new HashMap<>();
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800160
161 @GuardedBy("mLock")
Marin Shalamanove85bd562020-04-24 17:21:32 +0200162 private Map<Integer, CarUxRestrictionsConfiguration> mCarUxRestrictionsConfigurations;
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800163
164 @GuardedBy("mLock")
Marin Shalamanove85bd562020-04-24 17:21:32 +0200165 private Map<Integer, CarUxRestrictions> mCurrentUxRestrictions;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700166
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800167 @GuardedBy("mLock")
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800168 private String mRestrictionMode = UX_RESTRICTION_MODE_BASELINE;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700169
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800170 @GuardedBy("mLock")
171 private float mCurrentMovingSpeed;
172
Marin Shalamanove85bd562020-04-24 17:21:32 +0200173 // Represents a physical port for display.
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800174 @GuardedBy("mLock")
Marin Shalamanove85bd562020-04-24 17:21:32 +0200175 private int mDefaultDisplayPhysicalPort;
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800176
177 @GuardedBy("mLock")
Marin Shalamanove85bd562020-04-24 17:21:32 +0200178 private final List<Integer> mPhysicalPorts = new ArrayList<>();
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800179
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700180 // Flag to disable broadcasting UXR changes - for development purposes
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800181 @GuardedBy("mLock")
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700182 private boolean mUxRChangeBroadcastEnabled = true;
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800183
Ram Periathiruvadi22c0d682018-05-10 17:50:48 -0700184 // For dumpsys logging
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800185 @GuardedBy("mLock")
Ram Periathiruvadi22c0d682018-05-10 17:50:48 -0700186 private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>();
187
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800188 public CarUxRestrictionsManagerService(Context context, CarDrivingStateService drvService,
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800189 CarPropertyService propertyService) {
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800190 mContext = context;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700191 mDisplayManager = mContext.getSystemService(DisplayManager.class);
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800192 mDrivingStateService = drvService;
Steve Paik4d257022018-04-27 13:28:31 -0700193 mCarPropertyService = propertyService;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800194 }
195
196 @Override
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800197 public void init() {
198 synchronized (mLock) {
199 mDefaultDisplayPhysicalPort = getDefaultDisplayPhysicalPort(mDisplayManager);
200 initPhysicalPort();
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700201
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800202 // Unrestricted until driving state information is received. During boot up, we don't
203 // want
204 // everything to be blocked until data is available from CarPropertyManager. If we
205 // start
206 // driving and we don't get speed or gear information, we have bigger problems.
207 mCurrentUxRestrictions = new HashMap<>();
Marin Shalamanove85bd562020-04-24 17:21:32 +0200208 for (int port : mPhysicalPorts) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800209 mCurrentUxRestrictions.put(port, createUnrestrictedRestrictions());
210 }
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700211
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800212 // Load the prod config, or if there is a staged one, promote that first only if the
213 // current driving state, as provided by the driving state service, is parked.
214 mCarUxRestrictionsConfigurations = convertToMap(loadConfig());
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700215 }
216
Jordan Jozwiakef172992019-07-31 17:53:12 -0700217 // subscribe to driving state changes
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800218 mDrivingStateService.registerDrivingStateChangeListener(
219 mICarDrivingStateChangeEventListener);
Steve Paik4d257022018-04-27 13:28:31 -0700220 // subscribe to property service for speed
221 mCarPropertyService.registerListener(VehicleProperty.PERF_VEHICLE_SPEED,
222 PROPERTY_UPDATE_RATE, mICarPropertyEventListener);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700223
Ram Periathiruvadif6381fb2018-06-05 18:44:19 -0700224 initializeUxRestrictions();
225 }
226
Wenting Zhai29021312019-01-09 12:11:14 -0800227 @Override
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700228 public List<CarUxRestrictionsConfiguration> getConfigs() {
Jordan Jozwiak0bceb1d2019-08-07 18:00:59 -0700229 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800230 synchronized (mLock) {
231 return new ArrayList<>(mCarUxRestrictionsConfigurations.values());
232 }
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800233 }
234
235 /**
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700236 * Loads UX restrictions configurations and returns them.
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800237 *
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800238 * <p>Reads config from the following sources in order:
239 * <ol>
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800240 * <li>saved config set by {@link #saveUxRestrictionsConfigurationForNextBoot(List)};
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800241 * <li>XML resource config from {@code R.xml.car_ux_restrictions_map};
242 * <li>hardcoded default config.
243 * </ol>
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800244 *
Jordan Jozwiakef172992019-07-31 17:53:12 -0700245 * This method attempts to promote staged config file, which requires getting the current
246 * driving state.
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800247 */
248 @VisibleForTesting
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800249 List<CarUxRestrictionsConfiguration> loadConfig() {
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800250 promoteStagedConfig();
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700251 List<CarUxRestrictionsConfiguration> configs;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800252
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800253 // Production config, if available, is the first choice.
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800254 File prodConfig = getFile(CONFIG_FILENAME_PRODUCTION);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800255 if (prodConfig.exists()) {
256 logd("Attempting to read production config");
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700257 configs = readPersistedConfig(prodConfig);
258 if (configs != null) {
259 return configs;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800260 }
261 }
262
263 // XML config is the second choice.
264 logd("Attempting to read config from XML resource");
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700265 configs = readXmlConfig();
266 if (configs != null) {
267 return configs;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800268 }
269
270 // This should rarely happen.
Eric Jeongbd5fb562020-12-21 13:49:40 -0800271 Slog.w(TAG, "Creating default config");
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700272
273 configs = new ArrayList<>();
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800274 synchronized (mLock) {
Marin Shalamanove85bd562020-04-24 17:21:32 +0200275 for (int port : mPhysicalPorts) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800276 configs.add(createDefaultConfig(port));
277 }
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700278 }
279 return configs;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800280 }
281
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800282 private File getFile(String filename) {
283 SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
284 return new File(systemInterface.getSystemCarDir(), filename);
285 }
286
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800287 @Nullable
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700288 private List<CarUxRestrictionsConfiguration> readXmlConfig() {
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800289 try {
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700290 return CarUxRestrictionsConfigurationXmlParser.parse(
291 mContext, R.xml.car_ux_restrictions_map);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800292 } catch (IOException | XmlPullParserException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800293 Slog.e(TAG, "Could not read config from XML resource", e);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800294 }
295 return null;
296 }
297
Jordan Jozwiakef172992019-07-31 17:53:12 -0700298 /**
299 * Promotes the staged config to prod, by replacing the prod file. Only do this if the car is
300 * parked to avoid changing the restrictions during a drive.
301 */
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800302 private void promoteStagedConfig() {
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800303 Path stagedConfig = getFile(CONFIG_FILENAME_STAGED).toPath();
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800304
305 CarDrivingStateEvent currentDrivingStateEvent =
306 mDrivingStateService.getCurrentDrivingState();
307 // Only promote staged config when car is parked.
308 if (currentDrivingStateEvent != null
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700309 && currentDrivingStateEvent.eventValue == DRIVING_STATE_PARKED
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800310 && Files.exists(stagedConfig)) {
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800311
312 Path prod = getFile(CONFIG_FILENAME_PRODUCTION).toPath();
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800313 try {
314 logd("Attempting to promote stage config");
315 Files.move(stagedConfig, prod, REPLACE_EXISTING);
316 } catch (IOException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800317 Slog.e(TAG, "Could not promote state config", e);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800318 }
319 }
320 }
321
Ram Periathiruvadif6381fb2018-06-05 18:44:19 -0700322 // Update current restrictions by getting the current driving state and speed.
323 private void initializeUxRestrictions() {
324 CarDrivingStateEvent currentDrivingStateEvent =
325 mDrivingStateService.getCurrentDrivingState();
326 // if we don't have enough information from the CarPropertyService to compute the UX
327 // restrictions, then leave the UX restrictions unchanged from what it was initialized to
328 // in the constructor.
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700329 if (currentDrivingStateEvent == null
330 || currentDrivingStateEvent.eventValue == DRIVING_STATE_UNKNOWN) {
Ram Periathiruvadif6381fb2018-06-05 18:44:19 -0700331 return;
332 }
333 int currentDrivingState = currentDrivingStateEvent.eventValue;
334 Float currentSpeed = getCurrentSpeed();
335 if (currentSpeed == SPEED_NOT_AVAILABLE) {
336 return;
337 }
338 // At this point the underlying CarPropertyService has provided us enough information to
339 // compute the UX restrictions that could be potentially different from the initial UX
340 // restrictions.
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800341 synchronized (mLock) {
342 handleDispatchUxRestrictionsLocked(currentDrivingState, currentSpeed);
343 }
Ram Periathiruvadif6381fb2018-06-05 18:44:19 -0700344 }
345
346 private Float getCurrentSpeed() {
Kai Wang301dd472021-05-18 06:09:44 +0000347 CarPropertyValue value = mCarPropertyService.getPropertySafe(
348 VehicleProperty.PERF_VEHICLE_SPEED, 0);
Ram Periathiruvadif6381fb2018-06-05 18:44:19 -0700349 if (value != null) {
350 return (Float) value.getValue();
351 }
352 return SPEED_NOT_AVAILABLE;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800353 }
354
355 @Override
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800356 public void release() {
357 while (mUxRClients.getRegisteredCallbackCount() > 0) {
358 for (int i = mUxRClients.getRegisteredCallbackCount() - 1; i >= 0; i--) {
359 ICarUxRestrictionsChangeListener client = mUxRClients.getRegisteredCallbackItem(i);
360 if (client == null) {
361 continue;
362 }
363 mUxRClients.unregister(client);
364 }
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800365 }
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800366 mDrivingStateService.unregisterDrivingStateChangeListener(
367 mICarDrivingStateChangeEventListener);
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800368 synchronized (mLock) {
Yuncheol Heob9554e12019-10-08 15:51:49 -0700369 mActivityViewDisplayInfoMap.clear();
370 }
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800371 }
372
373 // Binder methods
374
375 /**
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700376 * Registers a {@link ICarUxRestrictionsChangeListener} to be notified for changes to the UX
377 * restrictions.
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800378 *
Jordan Jozwiakb711ace2019-05-21 14:24:23 -0700379 * @param listener Listener to register
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700380 * @param displayId UX restrictions on this display will be notified.
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800381 */
382 @Override
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800383 public void registerUxRestrictionsChangeListener(
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700384 ICarUxRestrictionsChangeListener listener, int displayId) {
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800385 if (listener == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800386 Slog.e(TAG, "registerUxRestrictionsChangeListener(): listener null");
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800387 throw new IllegalArgumentException("Listener is null");
388 }
Marin Shalamanove85bd562020-04-24 17:21:32 +0200389 Integer physicalPort;
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800390 synchronized (mLock) {
391 physicalPort = getPhysicalPortLocked(displayId);
392 if (physicalPort == null) {
393 physicalPort = mDefaultDisplayPhysicalPort;
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800394 }
395 }
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800396 mUxRClients.register(listener, new RemoteCallbackListCookie(physicalPort));
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800397 }
398
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800399 /**
400 * Unregister the given UX Restrictions listener
401 *
402 * @param listener client to unregister
403 */
404 @Override
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800405 public void unregisterUxRestrictionsChangeListener(ICarUxRestrictionsChangeListener listener) {
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800406 if (listener == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800407 Slog.e(TAG, "unregisterUxRestrictionsChangeListener(): listener null");
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800408 throw new IllegalArgumentException("Listener is null");
409 }
410
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800411 mUxRClients.unregister(listener);
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800412 }
413
414 /**
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700415 * Gets the current UX restrictions for a display.
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800416 *
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700417 * @param displayId UX restrictions on this display will be returned.
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800418 */
419 @Override
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800420 public CarUxRestrictions getCurrentUxRestrictions(int displayId) {
Jordan Jozwiak5465ca92020-02-03 12:04:12 -0800421 CarUxRestrictions restrictions;
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800422 synchronized (mLock) {
Felipe Leme56ef9ad2020-11-05 18:39:03 -0800423 if (mCurrentUxRestrictions == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800424 Slog.wtf(TAG, "getCurrentUxRestrictions() called before init()");
Felipe Leme56ef9ad2020-11-05 18:39:03 -0800425 return null;
426 }
Jordan Jozwiak5465ca92020-02-03 12:04:12 -0800427 restrictions = mCurrentUxRestrictions.get(getPhysicalPortLocked(displayId));
428 }
Jordan Jozwiakb711ace2019-05-21 14:24:23 -0700429 if (restrictions == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800430 Slog.e(TAG, "Restrictions are null for displayId:" + displayId
431 + ". Returning full restrictions.");
Jordan Jozwiakb711ace2019-05-21 14:24:23 -0700432 restrictions = createFullyRestrictedRestrictions();
433 }
434 return restrictions;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700435 }
436
437 /**
438 * Convenience method to retrieve restrictions for default display.
439 */
440 @Nullable
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800441 public CarUxRestrictions getCurrentUxRestrictions() {
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700442 return getCurrentUxRestrictions(Display.DEFAULT_DISPLAY);
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800443 }
444
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800445 @Override
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800446 public boolean saveUxRestrictionsConfigurationForNextBoot(
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700447 List<CarUxRestrictionsConfiguration> configs) {
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800448 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700449
450 validateConfigs(configs);
451
452 return persistConfig(configs, CONFIG_FILENAME_STAGED);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800453 }
454
Wenting Zhai29021312019-01-09 12:11:14 -0800455 @Override
456 @Nullable
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700457 public List<CarUxRestrictionsConfiguration> getStagedConfigs() {
Jordan Jozwiak0bceb1d2019-08-07 18:00:59 -0700458 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
459
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800460 File stagedConfig = getFile(CONFIG_FILENAME_STAGED);
Wenting Zhai29021312019-01-09 12:11:14 -0800461 if (stagedConfig.exists()) {
462 logd("Attempting to read staged config");
463 return readPersistedConfig(stagedConfig);
464 } else {
465 return null;
466 }
467 }
468
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800469 /**
Yao, Yuxing8f475c12019-02-25 16:25:01 -0800470 * Sets the restriction mode to use. Restriction mode allows a different set of restrictions to
471 * be applied in the same driving state. Restrictions for each mode can be configured through
472 * {@link CarUxRestrictionsConfiguration}.
473 *
474 * <p>Defaults to {@link CarUxRestrictionsManager#UX_RESTRICTION_MODE_BASELINE}.
475 *
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800476 * @param mode the restriction mode
Yao, Yuxing8f475c12019-02-25 16:25:01 -0800477 * @return {@code true} if mode was successfully changed; {@code false} otherwise.
Yao, Yuxing8f475c12019-02-25 16:25:01 -0800478 * @see CarUxRestrictionsConfiguration.DrivingStateRestrictions
479 * @see CarUxRestrictionsConfiguration.Builder
480 */
481 @Override
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800482 public boolean setRestrictionMode(@NonNull String mode) {
Yao, Yuxingee931b42019-04-08 10:09:46 -0700483 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800484 Objects.requireNonNull(mode, "mode must not be null");
Yao, Yuxingee931b42019-04-08 10:09:46 -0700485
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800486 synchronized (mLock) {
487 if (mRestrictionMode.equals(mode)) {
488 return true;
489 }
490
491 addTransitionLogLocked(TAG, mRestrictionMode, mode, System.currentTimeMillis(),
492 "Restriction mode");
493 mRestrictionMode = mode;
494 logd("Set restriction mode to: " + mode);
495
496 handleDispatchUxRestrictionsLocked(
497 mDrivingStateService.getCurrentDrivingState().eventValue, getCurrentSpeed());
Yao, Yuxing8f475c12019-02-25 16:25:01 -0800498 }
Yao, Yuxing8f475c12019-02-25 16:25:01 -0800499 return true;
500 }
501
502 @Override
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800503 @NonNull
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800504 public String getRestrictionMode() {
Yao, Yuxingee931b42019-04-08 10:09:46 -0700505 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION);
506
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800507 synchronized (mLock) {
508 return mRestrictionMode;
509 }
Yao, Yuxing8f475c12019-02-25 16:25:01 -0800510 }
511
512 /**
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800513 * Writes configuration into the specified file.
514 *
515 * IO access on file is not thread safe. Caller should ensure threading protection.
516 */
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700517 private boolean persistConfig(List<CarUxRestrictionsConfiguration> configs, String filename) {
Yao, Yuxing9bfb7492019-02-15 11:53:34 -0800518 File file = getFile(filename);
519 AtomicFile stagedFile = new AtomicFile(file);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800520 FileOutputStream fos;
521 try {
522 fos = stagedFile.startWrite();
523 } catch (IOException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800524 Slog.e(TAG, "Could not open file to persist config", e);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800525 return false;
526 }
527 try (JsonWriter jsonWriter = new JsonWriter(
528 new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
Jordan Jozwiak5465ca92020-02-03 12:04:12 -0800529 writeJson(jsonWriter, configs);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800530 } catch (IOException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800531 Slog.e(TAG, "Could not persist config", e);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800532 stagedFile.failWrite(fos);
533 return false;
534 }
535 stagedFile.finishWrite(fos);
536 return true;
537 }
538
Jordan Jozwiak5465ca92020-02-03 12:04:12 -0800539 @VisibleForTesting
540 void writeJson(JsonWriter jsonWriter, List<CarUxRestrictionsConfiguration> configs)
541 throws IOException {
542 jsonWriter.beginObject();
543 jsonWriter.name(JSON_NAME_SCHEMA_VERSION).value(JSON_SCHEMA_VERSION_V2);
544 jsonWriter.name(JSON_NAME_RESTRICTIONS);
545 jsonWriter.beginArray();
546 for (CarUxRestrictionsConfiguration config : configs) {
547 config.writeJson(jsonWriter);
548 }
549 jsonWriter.endArray();
550 jsonWriter.endObject();
551 }
552
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800553 @Nullable
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700554 private List<CarUxRestrictionsConfiguration> readPersistedConfig(File file) {
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800555 if (!file.exists()) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800556 Slog.e(TAG, "Could not find config file: " + file.getName());
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800557 return null;
558 }
559
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800560 // Take one pass at the file to check the version and then a second pass to read the
561 // contents. We could assess the version and read in one pass, but we're preferring
562 // clarity over complexity here.
563 int schemaVersion = readFileSchemaVersion(file);
564
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700565 AtomicFile configFile = new AtomicFile(file);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800566 try (JsonReader reader = new JsonReader(
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700567 new InputStreamReader(configFile.openRead(), StandardCharsets.UTF_8))) {
568 List<CarUxRestrictionsConfiguration> configs = new ArrayList<>();
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800569 switch (schemaVersion) {
570 case JSON_SCHEMA_VERSION_V1:
571 readV1Json(reader, configs);
572 break;
573 case JSON_SCHEMA_VERSION_V2:
574 readV2Json(reader, configs);
575 break;
576 default:
Eric Jeongbd5fb562020-12-21 13:49:40 -0800577 Slog.e(TAG, "Unable to parse schema for version " + schemaVersion);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700578 }
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800579
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700580 return configs;
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800581 } catch (IOException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800582 Slog.e(TAG, "Could not read persisted config file " + file.getName(), e);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800583 }
584 return null;
585 }
586
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800587 private void readV1Json(JsonReader reader,
588 List<CarUxRestrictionsConfiguration> configs) throws IOException {
589 readRestrictionsArray(reader, configs, JSON_SCHEMA_VERSION_V1);
590 }
591
592 private void readV2Json(JsonReader reader,
593 List<CarUxRestrictionsConfiguration> configs) throws IOException {
594 reader.beginObject();
595 while (reader.hasNext()) {
596 String name = reader.nextName();
597 switch (name) {
598 case JSON_NAME_RESTRICTIONS:
599 readRestrictionsArray(reader, configs, JSON_SCHEMA_VERSION_V2);
600 break;
601 default:
602 reader.skipValue();
603 }
604 }
605 reader.endObject();
606 }
607
608 private int readFileSchemaVersion(File file) {
609 AtomicFile configFile = new AtomicFile(file);
610 try (JsonReader reader = new JsonReader(
611 new InputStreamReader(configFile.openRead(), StandardCharsets.UTF_8))) {
612 List<CarUxRestrictionsConfiguration> configs = new ArrayList<>();
613 if (reader.peek() == JsonToken.BEGIN_ARRAY) {
614 // only schema V1 beings with an array - no need to keep reading
615 reader.close();
616 return JSON_SCHEMA_VERSION_V1;
617 } else {
618 reader.beginObject();
619 while (reader.hasNext()) {
620 String name = reader.nextName();
621 switch (name) {
622 case JSON_NAME_SCHEMA_VERSION:
623 int schemaVersion = reader.nextInt();
624 // got the version, no need to continue reading
625 reader.close();
626 return schemaVersion;
627 default:
628 reader.skipValue();
629 }
630 }
631 reader.endObject();
632 }
633 } catch (IOException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800634 Slog.e(TAG, "Could not read persisted config file " + file.getName(), e);
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -0800635 }
636 return UNKNOWN_JSON_SCHEMA_VERSION;
637 }
638
639 private void readRestrictionsArray(JsonReader reader,
640 List<CarUxRestrictionsConfiguration> configs, @JsonSchemaVersion int schemaVersion)
641 throws IOException {
642 reader.beginArray();
643 while (reader.hasNext()) {
644 configs.add(CarUxRestrictionsConfiguration.readJson(reader, schemaVersion));
645 }
646 reader.endArray();
647 }
648
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800649 /**
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700650 * Enable/disable UX restrictions change broadcast blocking.
651 * Setting this to true will stop broadcasts of UX restriction change to listeners.
652 * This method works only on debug builds and the caller of this method needs to have the same
653 * signature of the car service.
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700654 */
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800655 public void setUxRChangeBroadcastEnabled(boolean enable) {
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700656 if (!isDebugBuild()) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800657 Slog.e(TAG, "Cannot set UX restriction change broadcast.");
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700658 return;
659 }
660 // Check if the caller has the same signature as that of the car service.
661 if (mContext.getPackageManager().checkSignatures(Process.myUid(), Binder.getCallingUid())
662 != PackageManager.SIGNATURE_MATCH) {
663 throw new SecurityException(
664 "Caller " + mContext.getPackageManager().getNameForUid(Binder.getCallingUid())
665 + " does not have the right signature");
666 }
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800667
668 synchronized (mLock) {
669 if (enable) {
670 // if enabling it back, send the current restrictions
671 mUxRChangeBroadcastEnabled = enable;
672 handleDispatchUxRestrictionsLocked(
673 mDrivingStateService.getCurrentDrivingState().eventValue,
674 getCurrentSpeed());
675 } else {
676 // fake parked state, so if the system is currently restricted, the restrictions are
677 // relaxed.
678 handleDispatchUxRestrictionsLocked(DRIVING_STATE_PARKED, 0);
679 mUxRChangeBroadcastEnabled = enable;
680 }
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700681 }
682 }
683
684 private boolean isDebugBuild() {
685 return Build.IS_USERDEBUG || Build.IS_ENG;
686 }
687
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800688 @Override
Felipe Leme176a5fd2021-01-20 15:48:33 -0800689 public void dump(IndentingPrintWriter writer) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800690 synchronized (mLock) {
691 writer.println("*CarUxRestrictionsManagerService*");
692 mUxRClients.dump(writer, "UX Restrictions Clients ");
Marin Shalamanove85bd562020-04-24 17:21:32 +0200693 for (int port : mCurrentUxRestrictions.keySet()) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800694 CarUxRestrictions restrictions = mCurrentUxRestrictions.get(port);
695 writer.printf("Port: 0x%02X UXR: %s\n", port, restrictions.toString());
696 }
697 if (isDebugBuild()) {
698 writer.println("mUxRChangeBroadcastEnabled? " + mUxRChangeBroadcastEnabled);
699 }
700 writer.println("UX Restriction configurations:");
701 for (CarUxRestrictionsConfiguration config :
702 mCarUxRestrictionsConfigurations.values()) {
703 config.dump(writer);
704 }
705 writer.println("UX Restriction change log:");
706 for (Utils.TransitionLog tlog : mTransitionLogs) {
707 writer.println(tlog);
708 }
709 writer.println("UX Restriction display info:");
710 for (int i = mActivityViewDisplayInfoMap.size() - 1; i >= 0; --i) {
711 DisplayInfo info = mActivityViewDisplayInfoMap.valueAt(i);
712 writer.printf("Display%d: physicalDisplayId=%d, owner=%s\n",
713 mActivityViewDisplayInfoMap.keyAt(i), info.mPhysicalDisplayId, info.mOwner);
714 }
Yuncheol Heob9554e12019-10-08 15:51:49 -0700715 }
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800716 }
717
718 /**
719 * {@link CarDrivingStateEvent} listener registered with the {@link CarDrivingStateService}
720 * for getting driving state change notifications.
721 */
722 private final ICarDrivingStateChangeListener mICarDrivingStateChangeEventListener =
723 new ICarDrivingStateChangeListener.Stub() {
724 @Override
725 public void onDrivingStateChanged(CarDrivingStateEvent event) {
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800726 logd("Driving State Changed:" + event.eventValue);
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800727 synchronized (mLock) {
728 handleDrivingStateEventLocked(event);
729 }
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800730 }
731 };
732
733 /**
734 * Handle the driving state change events coming from the {@link CarDrivingStateService}.
735 * Map the driving state to the corresponding UX Restrictions and dispatch the
736 * UX Restriction change to the registered clients.
737 */
Jordan Jozwiak5465ca92020-02-03 12:04:12 -0800738 @VisibleForTesting
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800739 @GuardedBy("mLock")
740 void handleDrivingStateEventLocked(CarDrivingStateEvent event) {
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800741 if (event == null) {
742 return;
743 }
744 int drivingState = event.eventValue;
Ram Periathiruvadif6381fb2018-06-05 18:44:19 -0700745 Float speed = getCurrentSpeed();
746
747 if (speed != SPEED_NOT_AVAILABLE) {
748 mCurrentMovingSpeed = speed;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700749 } else if (drivingState == DRIVING_STATE_PARKED
750 || drivingState == DRIVING_STATE_UNKNOWN) {
Ram Periathiruvadiec551ae2018-03-15 11:38:07 -0700751 // If speed is unavailable, but the driving state is parked or unknown, it can still be
752 // handled.
Yao, Yuxing0811d5a2018-11-29 16:18:53 -0800753 logd("Speed null when driving state is: " + drivingState);
Ram Periathiruvadiec551ae2018-03-15 11:38:07 -0700754 mCurrentMovingSpeed = 0;
755 } else {
756 // If we get here with driving state != parked or unknown && speed == null,
757 // something is wrong. CarDrivingStateService could not have inferred idling or moving
758 // when speed is not available
Eric Jeongbd5fb562020-12-21 13:49:40 -0800759 Slog.e(TAG, "Unexpected: Speed null when driving state is: " + drivingState);
Ram Periathiruvadiec551ae2018-03-15 11:38:07 -0700760 return;
761 }
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800762 handleDispatchUxRestrictionsLocked(drivingState, mCurrentMovingSpeed);
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800763 }
764
765 /**
Steve Paik4d257022018-04-27 13:28:31 -0700766 * {@link CarPropertyEvent} listener registered with the {@link CarPropertyService} for getting
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800767 * speed change notifications.
768 */
Steve Paik4d257022018-04-27 13:28:31 -0700769 private final ICarPropertyEventListener mICarPropertyEventListener =
770 new ICarPropertyEventListener.Stub() {
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800771 @Override
Steve Paik4d257022018-04-27 13:28:31 -0700772 public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800773 synchronized (mLock) {
774 for (CarPropertyEvent event : events) {
775 if ((event.getEventType()
776 == CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE)
777 && (event.getCarPropertyValue().getPropertyId()
778 == VehicleProperty.PERF_VEHICLE_SPEED)) {
779 handleSpeedChangeLocked(
780 (Float) event.getCarPropertyValue().getValue());
781 }
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800782 }
783 }
784 }
785 };
786
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800787 @GuardedBy("mLock")
788 private void handleSpeedChangeLocked(float newSpeed) {
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800789 if (newSpeed == mCurrentMovingSpeed) {
790 // Ignore if speed hasn't changed
791 return;
792 }
793 int currentDrivingState = mDrivingStateService.getCurrentDrivingState().eventValue;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700794 if (currentDrivingState != DRIVING_STATE_MOVING) {
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800795 // Ignore speed changes if the vehicle is not moving
796 return;
797 }
798 mCurrentMovingSpeed = newSpeed;
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800799 handleDispatchUxRestrictionsLocked(currentDrivingState, newSpeed);
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800800 }
801
802 /**
803 * Handle dispatching UX restrictions change.
804 *
805 * @param currentDrivingState driving state of the vehicle
806 * @param speed speed of the vehicle
807 */
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800808 @GuardedBy("mLock")
809 private void handleDispatchUxRestrictionsLocked(@CarDrivingState int currentDrivingState,
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800810 float speed) {
Daulet Zhanguzin4ded6782020-01-03 15:47:10 +0000811 Objects.requireNonNull(mCarUxRestrictionsConfigurations,
Jordan Jozwiakef172992019-07-31 17:53:12 -0700812 "mCarUxRestrictionsConfigurations must be initialized");
Daulet Zhanguzin4ded6782020-01-03 15:47:10 +0000813 Objects.requireNonNull(mCurrentUxRestrictions,
Jordan Jozwiakef172992019-07-31 17:53:12 -0700814 "mCurrentUxRestrictions must be initialized");
815
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700816 if (isDebugBuild() && !mUxRChangeBroadcastEnabled) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800817 Slog.d(TAG, "Not dispatching UX Restriction due to setting");
Ram Periathiruvadicdee1202018-07-17 18:01:31 -0700818 return;
819 }
820
Marin Shalamanove85bd562020-04-24 17:21:32 +0200821 Map<Integer, CarUxRestrictions> newUxRestrictions = new HashMap<>();
822 for (int port : mPhysicalPorts) {
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700823 CarUxRestrictionsConfiguration config = mCarUxRestrictionsConfigurations.get(port);
Keun young Park4aa19c22019-05-03 18:25:01 -0700824 if (config == null) {
825 continue;
826 }
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800827
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700828 CarUxRestrictions uxRestrictions = config.getUxRestrictions(
829 currentDrivingState, speed, mRestrictionMode);
830 logd(String.format("Display port 0x%02x\tDO old->new: %b -> %b",
831 port,
832 mCurrentUxRestrictions.get(port).isRequiresDistractionOptimization(),
Ram Periathiruvadi12f42432018-03-28 19:47:37 -0700833 uxRestrictions.isRequiresDistractionOptimization()));
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700834 logd(String.format("Display port 0x%02x\tUxR old->new: 0x%x -> 0x%x",
835 port,
836 mCurrentUxRestrictions.get(port).getActiveRestrictions(),
Ram Periathiruvadi12f42432018-03-28 19:47:37 -0700837 uxRestrictions.getActiveRestrictions()));
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700838 newUxRestrictions.put(port, uxRestrictions);
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800839 }
Ram Periathiruvadi4526a432018-01-24 13:00:54 -0800840
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700841 // Ignore dispatching if the restrictions has not changed.
Marin Shalamanove85bd562020-04-24 17:21:32 +0200842 Set<Integer> displayToDispatch = new ArraySet<>();
843 for (int port : newUxRestrictions.keySet()) {
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700844 if (!mCurrentUxRestrictions.containsKey(port)) {
845 // This should never happen.
Eric Jeongbd5fb562020-12-21 13:49:40 -0800846 Slog.wtf(TAG, "Unrecognized port:" + port);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700847 continue;
848 }
849 CarUxRestrictions uxRestrictions = newUxRestrictions.get(port);
850 if (!mCurrentUxRestrictions.get(port).isSameRestrictions(uxRestrictions)) {
851 displayToDispatch.add(port);
852 }
853 }
854 if (displayToDispatch.isEmpty()) {
Ram Periathiruvadied9e8f82018-03-07 11:50:49 -0800855 return;
856 }
Ram Periathiruvadi22c0d682018-05-10 17:50:48 -0700857
Marin Shalamanove85bd562020-04-24 17:21:32 +0200858 for (int port : displayToDispatch) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800859 addTransitionLogLocked(
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700860 mCurrentUxRestrictions.get(port), newUxRestrictions.get(port));
861 }
862
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800863 dispatchRestrictionsToClients(newUxRestrictions, displayToDispatch);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700864
865 mCurrentUxRestrictions = newUxRestrictions;
866 }
867
Marin Shalamanove85bd562020-04-24 17:21:32 +0200868 private void dispatchRestrictionsToClients(Map<Integer, CarUxRestrictions> displayRestrictions,
869 Set<Integer> displayToDispatch) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800870 logd("dispatching to clients");
871 boolean success = mClientDispatchHandler.post(() -> {
872 int numClients = mUxRClients.beginBroadcast();
873 for (int i = 0; i < numClients; i++) {
874 ICarUxRestrictionsChangeListener callback = mUxRClients.getBroadcastItem(i);
875 RemoteCallbackListCookie cookie =
876 (RemoteCallbackListCookie) mUxRClients.getBroadcastCookie(i);
877 if (!displayToDispatch.contains(cookie.mPhysicalPort)) {
878 continue;
879 }
880 CarUxRestrictions restrictions = displayRestrictions.get(cookie.mPhysicalPort);
881 if (restrictions == null) {
882 // don't dispatch to displays without configurations
883 continue;
884 }
885 try {
886 callback.onUxRestrictionsChanged(restrictions);
887 } catch (RemoteException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800888 Slog.e(TAG,
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800889 String.format("Dispatch to listener %s failed for restrictions (%s)",
890 callback, restrictions));
891 }
892 }
893 mUxRClients.finishBroadcast();
894 });
895
896 if (!success) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800897 Slog.e(TAG, "Unable to post (" + displayRestrictions + ") event to dispatch handler");
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800898 }
899 }
900
Jordan Jozwiake1a84592020-02-28 12:55:30 -0800901 @VisibleForTesting
Marin Shalamanove85bd562020-04-24 17:21:32 +0200902 static int getDefaultDisplayPhysicalPort(DisplayManager displayManager) {
Jordan Jozwiake1a84592020-02-28 12:55:30 -0800903 Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700904 DisplayAddress.Physical address = (DisplayAddress.Physical) defaultDisplay.getAddress();
905
906 if (address == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800907 Slog.e(TAG, "Default display does not have physical display port. Using 0 as port.");
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700908 return DEFAULT_PORT;
909 }
910 return address.getPort();
911 }
912
913 private void initPhysicalPort() {
914 for (Display display : mDisplayManager.getDisplays()) {
915 if (display.getType() == Display.TYPE_VIRTUAL) {
916 continue;
917 }
918
919 if (display.getDisplayId() == Display.DEFAULT_DISPLAY && display.getAddress() == null) {
920 // Assume default display is a physical display so assign an address if it
921 // does not have one (possibly due to lower graphic driver version).
922 if (Log.isLoggable(TAG, Log.INFO)) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800923 Slog.i(TAG, "Default display does not have display address. Using default.");
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700924 }
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800925 synchronized (mLock) {
926 mPhysicalPorts.add(mDefaultDisplayPhysicalPort);
927 }
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700928 } else if (display.getAddress() instanceof DisplayAddress.Physical) {
Marin Shalamanove85bd562020-04-24 17:21:32 +0200929 int port = ((DisplayAddress.Physical) display.getAddress()).getPort();
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700930 if (Log.isLoggable(TAG, Log.INFO)) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800931 Slog.i(TAG, "Display " + display.getDisplayId() + " uses port " + port);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700932 }
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800933 synchronized (mLock) {
934 mPhysicalPorts.add(port);
935 }
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700936 } else {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800937 Slog.w(TAG, "At init non-virtual display has a non-physical display address: "
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700938 + display);
939 }
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -0800940 }
941 }
942
Marin Shalamanove85bd562020-04-24 17:21:32 +0200943 private Map<Integer, CarUxRestrictionsConfiguration> convertToMap(
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700944 List<CarUxRestrictionsConfiguration> configs) {
945 validateConfigs(configs);
946
Marin Shalamanove85bd562020-04-24 17:21:32 +0200947 Map<Integer, CarUxRestrictionsConfiguration> result = new HashMap<>();
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700948 if (configs.size() == 1) {
949 CarUxRestrictionsConfiguration config = configs.get(0);
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800950 synchronized (mLock) {
Marin Shalamanove85bd562020-04-24 17:21:32 +0200951 int port = config.getPhysicalPort() == null
Jordan Jozwiak576578a2020-02-27 17:06:24 -0800952 ? mDefaultDisplayPhysicalPort
953 : config.getPhysicalPort();
954 result.put(port, config);
955 }
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700956 } else {
957 for (CarUxRestrictionsConfiguration config : configs) {
958 result.put(config.getPhysicalPort(), config);
959 }
960 }
961 return result;
962 }
963
964 /**
965 * Validates configs for multi-display:
966 * - share the same restrictions parameters;
967 * - each sets display port;
968 * - each has unique display port.
969 */
970 @VisibleForTesting
971 void validateConfigs(List<CarUxRestrictionsConfiguration> configs) {
972 if (configs.size() == 0) {
973 throw new IllegalArgumentException("Empty configuration.");
974 }
975
976 if (configs.size() == 1) {
977 return;
978 }
979
980 CarUxRestrictionsConfiguration first = configs.get(0);
Marin Shalamanove85bd562020-04-24 17:21:32 +0200981 Set<Integer> existingPorts = new ArraySet<>();
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700982 for (CarUxRestrictionsConfiguration config : configs) {
983 if (!config.hasSameParameters(first)) {
984 // Input should have the same restriction parameters because:
985 // - it doesn't make sense otherwise; and
986 // - in format it matches how xml can only specify one set of parameters.
987 throw new IllegalArgumentException(
988 "Configurations should have the same restrictions parameters.");
989 }
990
Marin Shalamanove85bd562020-04-24 17:21:32 +0200991 Integer port = config.getPhysicalPort();
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -0700992 if (port == null) {
993 // Size was checked above; safe to assume there are multiple configs.
994 throw new IllegalArgumentException(
995 "Input contains multiple configurations; each must set physical port.");
996 }
997 if (existingPorts.contains(port)) {
998 throw new IllegalArgumentException("Multiple configurations for port " + port);
999 }
1000
1001 existingPorts.add(port);
1002 }
1003 }
1004
Jordan Jozwiakb711ace2019-05-21 14:24:23 -07001005 /**
Marin Shalamanove85bd562020-04-24 17:21:32 +02001006 * Returns the physical port id for the display or {@code null} if {@link
Jordan Jozwiakb711ace2019-05-21 14:24:23 -07001007 * DisplayManager#getDisplay(int)} is not aware of the provided id.
1008 */
1009 @Nullable
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001010 @GuardedBy("mLock")
Marin Shalamanove85bd562020-04-24 17:21:32 +02001011 private Integer getPhysicalPortLocked(int displayId) {
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001012 if (!mPortLookup.containsKey(displayId)) {
1013 Display display = mDisplayManager.getDisplay(displayId);
1014 if (display == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -08001015 Slog.w(TAG, "Could not retrieve display for id: " + displayId);
Jordan Jozwiakb711ace2019-05-21 14:24:23 -07001016 return null;
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001017 }
Marin Shalamanove85bd562020-04-24 17:21:32 +02001018 int port = doGetPhysicalPortLocked(display);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001019 mPortLookup.put(displayId, port);
1020 }
1021 return mPortLookup.get(displayId);
1022 }
1023
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001024 @GuardedBy("mLock")
Marin Shalamanove85bd562020-04-24 17:21:32 +02001025 private int doGetPhysicalPortLocked(@NonNull Display display) {
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001026 if (display.getType() == Display.TYPE_VIRTUAL) {
Eric Jeongbd5fb562020-12-21 13:49:40 -08001027 Slog.e(TAG, "Display " + display
Jordan Jozwiak5465ca92020-02-03 12:04:12 -08001028 + " is a virtual display and does not have a known port.");
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001029 return mDefaultDisplayPhysicalPort;
1030 }
1031
1032 DisplayAddress address = display.getAddress();
1033 if (address == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -08001034 Slog.e(TAG, "Display " + display
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001035 + " is not a virtual display but has null DisplayAddress.");
1036 return mDefaultDisplayPhysicalPort;
1037 } else if (!(address instanceof DisplayAddress.Physical)) {
Eric Jeongbd5fb562020-12-21 13:49:40 -08001038 Slog.e(TAG, "Display " + display + " has non-physical address: " + address);
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001039 return mDefaultDisplayPhysicalPort;
1040 } else {
1041 return ((DisplayAddress.Physical) address).getPort();
1042 }
1043 }
1044
1045 private CarUxRestrictions createUnrestrictedRestrictions() {
1046 return new CarUxRestrictions.Builder(/* reqOpt= */ false,
1047 CarUxRestrictions.UX_RESTRICTIONS_BASELINE, SystemClock.elapsedRealtimeNanos())
1048 .build();
1049 }
1050
Jordan Jozwiakb711ace2019-05-21 14:24:23 -07001051 private CarUxRestrictions createFullyRestrictedRestrictions() {
1052 return new CarUxRestrictions.Builder(
1053 /*reqOpt= */ true,
1054 CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED,
1055 SystemClock.elapsedRealtimeNanos()).build();
1056 }
1057
Marin Shalamanove85bd562020-04-24 17:21:32 +02001058 CarUxRestrictionsConfiguration createDefaultConfig(int port) {
Yao, Yuxing0811d5a2018-11-29 16:18:53 -08001059 return new CarUxRestrictionsConfiguration.Builder()
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001060 .setPhysicalPort(port)
1061 .setUxRestrictions(DRIVING_STATE_PARKED,
Wenting Zhai29021312019-01-09 12:11:14 -08001062 false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001063 .setUxRestrictions(DRIVING_STATE_IDLING,
Wenting Zhai29021312019-01-09 12:11:14 -08001064 false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001065 .setUxRestrictions(DRIVING_STATE_MOVING,
Wenting Zhai29021312019-01-09 12:11:14 -08001066 true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001067 .setUxRestrictions(DRIVING_STATE_UNKNOWN,
Wenting Zhai29021312019-01-09 12:11:14 -08001068 true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
Yao, Yuxing0811d5a2018-11-29 16:18:53 -08001069 .build();
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -08001070 }
Ram Periathiruvadi22c0d682018-05-10 17:50:48 -07001071
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001072 @GuardedBy("mLock")
1073 private void addTransitionLogLocked(String name, String from, String to, long timestamp,
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -08001074 String extra) {
Ram Periathiruvadi22c0d682018-05-10 17:50:48 -07001075 if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
1076 mTransitionLogs.remove();
1077 }
1078
1079 Utils.TransitionLog tLog = new Utils.TransitionLog(name, from, to, timestamp, extra);
1080 mTransitionLogs.add(tLog);
1081 }
1082
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001083 @GuardedBy("mLock")
1084 private void addTransitionLogLocked(
Yao, Yuxingc8bd72d2019-03-12 14:52:48 -07001085 CarUxRestrictions oldRestrictions, CarUxRestrictions newRestrictions) {
1086 if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
1087 mTransitionLogs.remove();
1088 }
1089 StringBuilder extra = new StringBuilder();
1090 extra.append(oldRestrictions.isRequiresDistractionOptimization() ? "DO -> " : "No DO -> ");
1091 extra.append(newRestrictions.isRequiresDistractionOptimization() ? "DO" : "No DO");
1092
1093 Utils.TransitionLog tLog = new Utils.TransitionLog(TAG,
1094 oldRestrictions.getActiveRestrictions(), newRestrictions.getActiveRestrictions(),
1095 System.currentTimeMillis(), extra.toString());
1096 mTransitionLogs.add(tLog);
1097 }
1098
Yao, Yuxing0811d5a2018-11-29 16:18:53 -08001099 private static void logd(String msg) {
1100 if (DBG) {
Yao, Yuxing9bfb7492019-02-15 11:53:34 -08001101 Slog.d(TAG, msg);
Yao, Yuxing0811d5a2018-11-29 16:18:53 -08001102 }
1103 }
Yuncheol Heob9554e12019-10-08 15:51:49 -07001104
Yuncheol Heob9554e12019-10-08 15:51:49 -07001105 private static final class DisplayInfo {
1106 final IRemoteCallback mOwner;
1107 final int mPhysicalDisplayId;
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -08001108
Yuncheol Heob9554e12019-10-08 15:51:49 -07001109 DisplayInfo(IRemoteCallback owner, int physicalDisplayId) {
1110 mOwner = owner;
1111 mPhysicalDisplayId = physicalDisplayId;
1112 }
Jordan Jozwiak0b0cf282020-01-17 12:26:42 -08001113 }
Yuncheol Heob9554e12019-10-08 15:51:49 -07001114
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001115 @GuardedBy("mLock")
Yuncheol Heob9554e12019-10-08 15:51:49 -07001116 private final SparseArray<DisplayInfo> mActivityViewDisplayInfoMap = new SparseArray<>();
1117
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001118 @GuardedBy("mLock")
Yuncheol Heob9554e12019-10-08 15:51:49 -07001119 private final RemoteCallbackList<IRemoteCallback> mRemoteCallbackList =
1120 new RemoteCallbackList<>() {
1121 @Override
1122 public void onCallbackDied(IRemoteCallback callback) {
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001123 synchronized (mLock) {
Yuncheol Heob9554e12019-10-08 15:51:49 -07001124 // Descending order to delete items safely from SpareArray.gc().
1125 for (int i = mActivityViewDisplayInfoMap.size() - 1; i >= 0; --i) {
1126 DisplayInfo info = mActivityViewDisplayInfoMap.valueAt(i);
1127 if (info.mOwner == callback) {
1128 logd("onCallbackDied: clean up callback=" + callback);
1129 mActivityViewDisplayInfoMap.removeAt(i);
Jordan Jozwiak5465ca92020-02-03 12:04:12 -08001130 mPortLookup.remove(mActivityViewDisplayInfoMap.keyAt(i));
Yuncheol Heob9554e12019-10-08 15:51:49 -07001131 }
1132 }
1133 }
1134 }
1135 };
1136
1137 @Override
1138 public void reportVirtualDisplayToPhysicalDisplay(IRemoteCallback callback,
1139 int virtualDisplayId, int physicalDisplayId) {
1140 logd("reportVirtualDisplayToPhysicalDisplay: callback=" + callback
1141 + ", virtualDisplayId=" + virtualDisplayId
1142 + ", physicalDisplayId=" + physicalDisplayId);
1143 boolean release = physicalDisplayId == Display.INVALID_DISPLAY;
1144 checkCallerOwnsDisplay(virtualDisplayId, release);
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001145 synchronized (mLock) {
Yuncheol Heob9554e12019-10-08 15:51:49 -07001146 if (release) {
1147 mRemoteCallbackList.unregister(callback);
1148 mActivityViewDisplayInfoMap.delete(virtualDisplayId);
Jordan Jozwiak5465ca92020-02-03 12:04:12 -08001149 mPortLookup.remove(virtualDisplayId);
Yuncheol Heob9554e12019-10-08 15:51:49 -07001150 return;
1151 }
1152 mRemoteCallbackList.register(callback);
1153 mActivityViewDisplayInfoMap.put(virtualDisplayId,
1154 new DisplayInfo(callback, physicalDisplayId));
Jordan Jozwiak7c7afc62020-06-12 18:01:40 -07001155 Integer physicalPort = getPhysicalPortLocked(physicalDisplayId);
1156 if (physicalPort == null) {
1157 // This should not happen.
Eric Jeongbd5fb562020-12-21 13:49:40 -08001158 Slog.wtf(TAG, "No known physicalPort for displayId:" + physicalDisplayId);
Jordan Jozwiak7c7afc62020-06-12 18:01:40 -07001159 physicalPort = mDefaultDisplayPhysicalPort;
1160 }
1161 mPortLookup.put(virtualDisplayId, physicalPort);
Yuncheol Heob9554e12019-10-08 15:51:49 -07001162 }
1163 }
1164
1165 @Override
1166 public int getMappedPhysicalDisplayOfVirtualDisplay(int displayId) {
1167 logd("getMappedPhysicalDisplayOfVirtualDisplay: displayId=" + displayId);
Jordan Jozwiak576578a2020-02-27 17:06:24 -08001168 synchronized (mLock) {
Yuncheol Heob9554e12019-10-08 15:51:49 -07001169 DisplayInfo foundInfo = mActivityViewDisplayInfoMap.get(displayId);
1170 if (foundInfo == null) {
1171 return Display.INVALID_DISPLAY;
1172 }
1173 // ActivityView can be placed in another ActivityView, so we should repeat the process
1174 // until no parent is found (reached to the physical display).
1175 while (foundInfo != null) {
1176 displayId = foundInfo.mPhysicalDisplayId;
1177 foundInfo = mActivityViewDisplayInfoMap.get(displayId);
1178 }
1179 }
1180 return displayId;
1181 }
1182
1183 private void checkCallerOwnsDisplay(int displayId, boolean release) {
1184 Display display = mDisplayManager.getDisplay(displayId);
1185 if (display == null) {
1186 // Bypasses the permission check for non-existing display when releasing it, since
1187 // reportVirtualDisplayToPhysicalDisplay() and releasing display happens simultaneously
1188 // and it's no harm to release the information on the non-existing display.
1189 if (release) return;
1190 throw new IllegalArgumentException(
1191 "Cannot find display for non-existent displayId: " + displayId);
1192 }
1193
1194 int callingUid = Binder.getCallingUid();
1195 int displayOwnerUid = display.getOwnerUid();
1196 if (callingUid != displayOwnerUid) {
1197 throw new SecurityException("The caller doesn't own the display: callingUid="
1198 + callingUid + ", displayOwnerUid=" + displayOwnerUid);
1199 }
1200 }
Ram Periathiruvadi25c16f12017-11-17 16:48:37 -08001201}