Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.car; |
| 18 | |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 19 | import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_IDLING; |
| 20 | import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING; |
| 21 | import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED; |
| 22 | import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_UNKNOWN; |
Yao, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 23 | import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE; |
| 24 | |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 25 | import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; |
| 26 | |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 27 | import android.annotation.IntDef; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 28 | import android.annotation.NonNull; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 29 | import android.annotation.Nullable; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 30 | import android.car.Car; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 31 | import android.car.drivingstate.CarDrivingStateEvent; |
| 32 | import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState; |
Ram Periathiruvadi | 4526a43 | 2018-01-24 13:00:54 -0800 | [diff] [blame] | 33 | import android.car.drivingstate.CarUxRestrictions; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 34 | import android.car.drivingstate.CarUxRestrictionsConfiguration; |
Yao, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 35 | import android.car.drivingstate.CarUxRestrictionsManager; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 36 | import android.car.drivingstate.ICarDrivingStateChangeListener; |
Ram Periathiruvadi | 4526a43 | 2018-01-24 13:00:54 -0800 | [diff] [blame] | 37 | import android.car.drivingstate.ICarUxRestrictionsChangeListener; |
| 38 | import android.car.drivingstate.ICarUxRestrictionsManager; |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 39 | import android.car.hardware.CarPropertyValue; |
| 40 | import android.car.hardware.property.CarPropertyEvent; |
| 41 | import android.car.hardware.property.ICarPropertyEventListener; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 42 | import android.content.Context; |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 43 | import android.content.pm.PackageManager; |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 44 | import android.hardware.automotive.vehicle.V2_0.VehicleProperty; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 45 | import android.hardware.display.DisplayManager; |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 46 | import android.os.Binder; |
| 47 | import android.os.Build; |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 48 | import android.os.Handler; |
| 49 | import android.os.HandlerThread; |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 50 | import android.os.IRemoteCallback; |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 51 | import android.os.Process; |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 52 | import android.os.RemoteCallbackList; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 53 | import android.os.RemoteException; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 54 | import android.os.SystemClock; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 55 | import android.util.ArraySet; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 56 | import android.util.AtomicFile; |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 57 | import android.util.IndentingPrintWriter; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 58 | import android.util.JsonReader; |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 59 | import android.util.JsonToken; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 60 | import android.util.JsonWriter; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 61 | import android.util.Log; |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 62 | import android.util.Slog; |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 63 | import android.util.SparseArray; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 64 | import android.view.Display; |
| 65 | import android.view.DisplayAddress; |
Ram Periathiruvadi | ec551ae | 2018-03-15 11:38:07 -0700 | [diff] [blame] | 66 | |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 67 | import com.android.car.systeminterface.SystemInterface; |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 68 | import com.android.internal.annotations.GuardedBy; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 69 | import com.android.internal.annotations.VisibleForTesting; |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 70 | |
Ram Periathiruvadi | ec551ae | 2018-03-15 11:38:07 -0700 | [diff] [blame] | 71 | import org.xmlpull.v1.XmlPullParserException; |
| 72 | |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 73 | import java.io.File; |
| 74 | import java.io.FileOutputStream; |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 75 | import java.io.IOException; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 76 | import java.io.InputStreamReader; |
| 77 | import java.io.OutputStreamWriter; |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 78 | import java.lang.annotation.Retention; |
| 79 | import java.lang.annotation.RetentionPolicy; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 80 | import java.nio.charset.StandardCharsets; |
| 81 | import java.nio.file.Files; |
| 82 | import java.nio.file.Path; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 83 | import java.util.ArrayList; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 84 | import java.util.HashMap; |
Ram Periathiruvadi | 22c0d68 | 2018-05-10 17:50:48 -0700 | [diff] [blame] | 85 | import java.util.LinkedList; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 86 | import java.util.List; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 87 | import java.util.Map; |
Daulet Zhanguzin | 4ded678 | 2020-01-03 15:47:10 +0000 | [diff] [blame] | 88 | import java.util.Objects; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 89 | import java.util.Set; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 90 | |
| 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, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 94 | * <p> |
| 95 | * <h1>UX Restrictions Configuration</h1> |
| 96 | * When this service starts, it will first try reading the configuration set through |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 97 | * {@link #saveUxRestrictionsConfigurationForNextBoot(List)}. |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 98 | * 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, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 101 | * <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 Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 105 | */ |
Ram Periathiruvadi | 4526a43 | 2018-01-24 13:00:54 -0800 | [diff] [blame] | 106 | public class CarUxRestrictionsManagerService extends ICarUxRestrictionsManager.Stub implements |
| 107 | CarServiceBase { |
Mayank Garg | 72c71d2 | 2021-02-03 23:54:45 -0800 | [diff] [blame] | 108 | private static final String TAG = CarLog.tagFor(CarUxRestrictionsManagerService.class); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 109 | private static final boolean DBG = false; |
Ram Periathiruvadi | 22c0d68 | 2018-05-10 17:50:48 -0700 | [diff] [blame] | 110 | private static final int MAX_TRANSITION_LOG_SIZE = 20; |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 111 | private static final int PROPERTY_UPDATE_RATE = 5; // Update rate in Hz |
Ram Periathiruvadi | f6381fb | 2018-06-05 18:44:19 -0700 | [diff] [blame] | 112 | private static final float SPEED_NOT_AVAILABLE = -1.0F; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 113 | |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 114 | 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 Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 124 | private static final int DEFAULT_PORT = 0; |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 125 | |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 126 | @VisibleForTesting |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 127 | static final String CONFIG_FILENAME_PRODUCTION = "ux_restrictions_prod_config.json"; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 128 | @VisibleForTesting |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 129 | static final String CONFIG_FILENAME_STAGED = "ux_restrictions_staged_config.json"; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 130 | |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 131 | private final Context mContext; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 132 | private final DisplayManager mDisplayManager; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 133 | private final CarDrivingStateService mDrivingStateService; |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 134 | private final CarPropertyService mCarPropertyService; |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 135 | private final HandlerThread mClientDispatchThread = CarServiceUtils.getHandlerThread( |
| 136 | getClass().getSimpleName()); |
| 137 | private final Handler mClientDispatchHandler = new Handler(mClientDispatchThread.getLooper()); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 138 | private final RemoteCallbackList<ICarUxRestrictionsChangeListener> mUxRClients = |
| 139 | new RemoteCallbackList<>(); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 140 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 141 | /** |
| 142 | * Metadata associated with a binder callback. |
| 143 | */ |
| 144 | private static class RemoteCallbackListCookie { |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 145 | final Integer mPhysicalPort; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 146 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 147 | RemoteCallbackListCookie(Integer physicalPort) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 148 | mPhysicalPort = physicalPort; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | private final Object mLock = new Object(); |
| 153 | |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 154 | /** |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 155 | * This lookup caches the mapping from an int display id to an int that represents a physical |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 156 | * port. It includes mappings for virtual displays. |
| 157 | */ |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 158 | @GuardedBy("mLock") |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 159 | private final Map<Integer, Integer> mPortLookup = new HashMap<>(); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 160 | |
| 161 | @GuardedBy("mLock") |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 162 | private Map<Integer, CarUxRestrictionsConfiguration> mCarUxRestrictionsConfigurations; |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 163 | |
| 164 | @GuardedBy("mLock") |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 165 | private Map<Integer, CarUxRestrictions> mCurrentUxRestrictions; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 166 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 167 | @GuardedBy("mLock") |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 168 | private String mRestrictionMode = UX_RESTRICTION_MODE_BASELINE; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 169 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 170 | @GuardedBy("mLock") |
| 171 | private float mCurrentMovingSpeed; |
| 172 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 173 | // Represents a physical port for display. |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 174 | @GuardedBy("mLock") |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 175 | private int mDefaultDisplayPhysicalPort; |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 176 | |
| 177 | @GuardedBy("mLock") |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 178 | private final List<Integer> mPhysicalPorts = new ArrayList<>(); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 179 | |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 180 | // Flag to disable broadcasting UXR changes - for development purposes |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 181 | @GuardedBy("mLock") |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 182 | private boolean mUxRChangeBroadcastEnabled = true; |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 183 | |
Ram Periathiruvadi | 22c0d68 | 2018-05-10 17:50:48 -0700 | [diff] [blame] | 184 | // For dumpsys logging |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 185 | @GuardedBy("mLock") |
Ram Periathiruvadi | 22c0d68 | 2018-05-10 17:50:48 -0700 | [diff] [blame] | 186 | private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>(); |
| 187 | |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 188 | public CarUxRestrictionsManagerService(Context context, CarDrivingStateService drvService, |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 189 | CarPropertyService propertyService) { |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 190 | mContext = context; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 191 | mDisplayManager = mContext.getSystemService(DisplayManager.class); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 192 | mDrivingStateService = drvService; |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 193 | mCarPropertyService = propertyService; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 194 | } |
| 195 | |
| 196 | @Override |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 197 | public void init() { |
| 198 | synchronized (mLock) { |
| 199 | mDefaultDisplayPhysicalPort = getDefaultDisplayPhysicalPort(mDisplayManager); |
| 200 | initPhysicalPort(); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 201 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 202 | // 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 Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 208 | for (int port : mPhysicalPorts) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 209 | mCurrentUxRestrictions.put(port, createUnrestrictedRestrictions()); |
| 210 | } |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 211 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 212 | // 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, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 215 | } |
| 216 | |
Jordan Jozwiak | ef17299 | 2019-07-31 17:53:12 -0700 | [diff] [blame] | 217 | // subscribe to driving state changes |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 218 | mDrivingStateService.registerDrivingStateChangeListener( |
| 219 | mICarDrivingStateChangeEventListener); |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 220 | // subscribe to property service for speed |
| 221 | mCarPropertyService.registerListener(VehicleProperty.PERF_VEHICLE_SPEED, |
| 222 | PROPERTY_UPDATE_RATE, mICarPropertyEventListener); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 223 | |
Ram Periathiruvadi | f6381fb | 2018-06-05 18:44:19 -0700 | [diff] [blame] | 224 | initializeUxRestrictions(); |
| 225 | } |
| 226 | |
Wenting Zhai | 2902131 | 2019-01-09 12:11:14 -0800 | [diff] [blame] | 227 | @Override |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 228 | public List<CarUxRestrictionsConfiguration> getConfigs() { |
Jordan Jozwiak | 0bceb1d | 2019-08-07 18:00:59 -0700 | [diff] [blame] | 229 | ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 230 | synchronized (mLock) { |
| 231 | return new ArrayList<>(mCarUxRestrictionsConfigurations.values()); |
| 232 | } |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 233 | } |
| 234 | |
| 235 | /** |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 236 | * Loads UX restrictions configurations and returns them. |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 237 | * |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 238 | * <p>Reads config from the following sources in order: |
| 239 | * <ol> |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 240 | * <li>saved config set by {@link #saveUxRestrictionsConfigurationForNextBoot(List)}; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 241 | * <li>XML resource config from {@code R.xml.car_ux_restrictions_map}; |
| 242 | * <li>hardcoded default config. |
| 243 | * </ol> |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 244 | * |
Jordan Jozwiak | ef17299 | 2019-07-31 17:53:12 -0700 | [diff] [blame] | 245 | * This method attempts to promote staged config file, which requires getting the current |
| 246 | * driving state. |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 247 | */ |
| 248 | @VisibleForTesting |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 249 | List<CarUxRestrictionsConfiguration> loadConfig() { |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 250 | promoteStagedConfig(); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 251 | List<CarUxRestrictionsConfiguration> configs; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 252 | |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 253 | // Production config, if available, is the first choice. |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 254 | File prodConfig = getFile(CONFIG_FILENAME_PRODUCTION); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 255 | if (prodConfig.exists()) { |
| 256 | logd("Attempting to read production config"); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 257 | configs = readPersistedConfig(prodConfig); |
| 258 | if (configs != null) { |
| 259 | return configs; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 260 | } |
| 261 | } |
| 262 | |
| 263 | // XML config is the second choice. |
| 264 | logd("Attempting to read config from XML resource"); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 265 | configs = readXmlConfig(); |
| 266 | if (configs != null) { |
| 267 | return configs; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 268 | } |
| 269 | |
| 270 | // This should rarely happen. |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 271 | Slog.w(TAG, "Creating default config"); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 272 | |
| 273 | configs = new ArrayList<>(); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 274 | synchronized (mLock) { |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 275 | for (int port : mPhysicalPorts) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 276 | configs.add(createDefaultConfig(port)); |
| 277 | } |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 278 | } |
| 279 | return configs; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 280 | } |
| 281 | |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 282 | private File getFile(String filename) { |
| 283 | SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class); |
| 284 | return new File(systemInterface.getSystemCarDir(), filename); |
| 285 | } |
| 286 | |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 287 | @Nullable |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 288 | private List<CarUxRestrictionsConfiguration> readXmlConfig() { |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 289 | try { |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 290 | return CarUxRestrictionsConfigurationXmlParser.parse( |
| 291 | mContext, R.xml.car_ux_restrictions_map); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 292 | } catch (IOException | XmlPullParserException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 293 | Slog.e(TAG, "Could not read config from XML resource", e); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 294 | } |
| 295 | return null; |
| 296 | } |
| 297 | |
Jordan Jozwiak | ef17299 | 2019-07-31 17:53:12 -0700 | [diff] [blame] | 298 | /** |
| 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, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 302 | private void promoteStagedConfig() { |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 303 | Path stagedConfig = getFile(CONFIG_FILENAME_STAGED).toPath(); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 304 | |
| 305 | CarDrivingStateEvent currentDrivingStateEvent = |
| 306 | mDrivingStateService.getCurrentDrivingState(); |
| 307 | // Only promote staged config when car is parked. |
| 308 | if (currentDrivingStateEvent != null |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 309 | && currentDrivingStateEvent.eventValue == DRIVING_STATE_PARKED |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 310 | && Files.exists(stagedConfig)) { |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 311 | |
| 312 | Path prod = getFile(CONFIG_FILENAME_PRODUCTION).toPath(); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 313 | try { |
| 314 | logd("Attempting to promote stage config"); |
| 315 | Files.move(stagedConfig, prod, REPLACE_EXISTING); |
| 316 | } catch (IOException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 317 | Slog.e(TAG, "Could not promote state config", e); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 318 | } |
| 319 | } |
| 320 | } |
| 321 | |
Ram Periathiruvadi | f6381fb | 2018-06-05 18:44:19 -0700 | [diff] [blame] | 322 | // 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, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 329 | if (currentDrivingStateEvent == null |
| 330 | || currentDrivingStateEvent.eventValue == DRIVING_STATE_UNKNOWN) { |
Ram Periathiruvadi | f6381fb | 2018-06-05 18:44:19 -0700 | [diff] [blame] | 331 | 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 Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 341 | synchronized (mLock) { |
| 342 | handleDispatchUxRestrictionsLocked(currentDrivingState, currentSpeed); |
| 343 | } |
Ram Periathiruvadi | f6381fb | 2018-06-05 18:44:19 -0700 | [diff] [blame] | 344 | } |
| 345 | |
| 346 | private Float getCurrentSpeed() { |
Kai Wang | 301dd47 | 2021-05-18 06:09:44 +0000 | [diff] [blame] | 347 | CarPropertyValue value = mCarPropertyService.getPropertySafe( |
| 348 | VehicleProperty.PERF_VEHICLE_SPEED, 0); |
Ram Periathiruvadi | f6381fb | 2018-06-05 18:44:19 -0700 | [diff] [blame] | 349 | if (value != null) { |
| 350 | return (Float) value.getValue(); |
| 351 | } |
| 352 | return SPEED_NOT_AVAILABLE; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 353 | } |
| 354 | |
| 355 | @Override |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 356 | 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 Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 365 | } |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 366 | mDrivingStateService.unregisterDrivingStateChangeListener( |
| 367 | mICarDrivingStateChangeEventListener); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 368 | synchronized (mLock) { |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 369 | mActivityViewDisplayInfoMap.clear(); |
| 370 | } |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 371 | } |
| 372 | |
| 373 | // Binder methods |
| 374 | |
| 375 | /** |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 376 | * Registers a {@link ICarUxRestrictionsChangeListener} to be notified for changes to the UX |
| 377 | * restrictions. |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 378 | * |
Jordan Jozwiak | b711ace | 2019-05-21 14:24:23 -0700 | [diff] [blame] | 379 | * @param listener Listener to register |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 380 | * @param displayId UX restrictions on this display will be notified. |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 381 | */ |
| 382 | @Override |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 383 | public void registerUxRestrictionsChangeListener( |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 384 | ICarUxRestrictionsChangeListener listener, int displayId) { |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 385 | if (listener == null) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 386 | Slog.e(TAG, "registerUxRestrictionsChangeListener(): listener null"); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 387 | throw new IllegalArgumentException("Listener is null"); |
| 388 | } |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 389 | Integer physicalPort; |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 390 | synchronized (mLock) { |
| 391 | physicalPort = getPhysicalPortLocked(displayId); |
| 392 | if (physicalPort == null) { |
| 393 | physicalPort = mDefaultDisplayPhysicalPort; |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 394 | } |
| 395 | } |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 396 | mUxRClients.register(listener, new RemoteCallbackListCookie(physicalPort)); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 397 | } |
| 398 | |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 399 | /** |
| 400 | * Unregister the given UX Restrictions listener |
| 401 | * |
| 402 | * @param listener client to unregister |
| 403 | */ |
| 404 | @Override |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 405 | public void unregisterUxRestrictionsChangeListener(ICarUxRestrictionsChangeListener listener) { |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 406 | if (listener == null) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 407 | Slog.e(TAG, "unregisterUxRestrictionsChangeListener(): listener null"); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 408 | throw new IllegalArgumentException("Listener is null"); |
| 409 | } |
| 410 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 411 | mUxRClients.unregister(listener); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 412 | } |
| 413 | |
| 414 | /** |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 415 | * Gets the current UX restrictions for a display. |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 416 | * |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 417 | * @param displayId UX restrictions on this display will be returned. |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 418 | */ |
| 419 | @Override |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 420 | public CarUxRestrictions getCurrentUxRestrictions(int displayId) { |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 421 | CarUxRestrictions restrictions; |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 422 | synchronized (mLock) { |
Felipe Leme | 56ef9ad | 2020-11-05 18:39:03 -0800 | [diff] [blame] | 423 | if (mCurrentUxRestrictions == null) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 424 | Slog.wtf(TAG, "getCurrentUxRestrictions() called before init()"); |
Felipe Leme | 56ef9ad | 2020-11-05 18:39:03 -0800 | [diff] [blame] | 425 | return null; |
| 426 | } |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 427 | restrictions = mCurrentUxRestrictions.get(getPhysicalPortLocked(displayId)); |
| 428 | } |
Jordan Jozwiak | b711ace | 2019-05-21 14:24:23 -0700 | [diff] [blame] | 429 | if (restrictions == null) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 430 | Slog.e(TAG, "Restrictions are null for displayId:" + displayId |
| 431 | + ". Returning full restrictions."); |
Jordan Jozwiak | b711ace | 2019-05-21 14:24:23 -0700 | [diff] [blame] | 432 | restrictions = createFullyRestrictedRestrictions(); |
| 433 | } |
| 434 | return restrictions; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 435 | } |
| 436 | |
| 437 | /** |
| 438 | * Convenience method to retrieve restrictions for default display. |
| 439 | */ |
| 440 | @Nullable |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 441 | public CarUxRestrictions getCurrentUxRestrictions() { |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 442 | return getCurrentUxRestrictions(Display.DEFAULT_DISPLAY); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 443 | } |
| 444 | |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 445 | @Override |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 446 | public boolean saveUxRestrictionsConfigurationForNextBoot( |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 447 | List<CarUxRestrictionsConfiguration> configs) { |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 448 | ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 449 | |
| 450 | validateConfigs(configs); |
| 451 | |
| 452 | return persistConfig(configs, CONFIG_FILENAME_STAGED); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 453 | } |
| 454 | |
Wenting Zhai | 2902131 | 2019-01-09 12:11:14 -0800 | [diff] [blame] | 455 | @Override |
| 456 | @Nullable |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 457 | public List<CarUxRestrictionsConfiguration> getStagedConfigs() { |
Jordan Jozwiak | 0bceb1d | 2019-08-07 18:00:59 -0700 | [diff] [blame] | 458 | ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION); |
| 459 | |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 460 | File stagedConfig = getFile(CONFIG_FILENAME_STAGED); |
Wenting Zhai | 2902131 | 2019-01-09 12:11:14 -0800 | [diff] [blame] | 461 | if (stagedConfig.exists()) { |
| 462 | logd("Attempting to read staged config"); |
| 463 | return readPersistedConfig(stagedConfig); |
| 464 | } else { |
| 465 | return null; |
| 466 | } |
| 467 | } |
| 468 | |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 469 | /** |
Yao, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 470 | * 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 Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 476 | * @param mode the restriction mode |
Yao, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 477 | * @return {@code true} if mode was successfully changed; {@code false} otherwise. |
Yao, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 478 | * @see CarUxRestrictionsConfiguration.DrivingStateRestrictions |
| 479 | * @see CarUxRestrictionsConfiguration.Builder |
| 480 | */ |
| 481 | @Override |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 482 | public boolean setRestrictionMode(@NonNull String mode) { |
Yao, Yuxing | ee931b4 | 2019-04-08 10:09:46 -0700 | [diff] [blame] | 483 | ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION); |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 484 | Objects.requireNonNull(mode, "mode must not be null"); |
Yao, Yuxing | ee931b4 | 2019-04-08 10:09:46 -0700 | [diff] [blame] | 485 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 486 | 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, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 498 | } |
Yao, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 499 | return true; |
| 500 | } |
| 501 | |
| 502 | @Override |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 503 | @NonNull |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 504 | public String getRestrictionMode() { |
Yao, Yuxing | ee931b4 | 2019-04-08 10:09:46 -0700 | [diff] [blame] | 505 | ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION); |
| 506 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 507 | synchronized (mLock) { |
| 508 | return mRestrictionMode; |
| 509 | } |
Yao, Yuxing | 8f475c1 | 2019-02-25 16:25:01 -0800 | [diff] [blame] | 510 | } |
| 511 | |
| 512 | /** |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 513 | * Writes configuration into the specified file. |
| 514 | * |
| 515 | * IO access on file is not thread safe. Caller should ensure threading protection. |
| 516 | */ |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 517 | private boolean persistConfig(List<CarUxRestrictionsConfiguration> configs, String filename) { |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 518 | File file = getFile(filename); |
| 519 | AtomicFile stagedFile = new AtomicFile(file); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 520 | FileOutputStream fos; |
| 521 | try { |
| 522 | fos = stagedFile.startWrite(); |
| 523 | } catch (IOException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 524 | Slog.e(TAG, "Could not open file to persist config", e); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 525 | return false; |
| 526 | } |
| 527 | try (JsonWriter jsonWriter = new JsonWriter( |
| 528 | new OutputStreamWriter(fos, StandardCharsets.UTF_8))) { |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 529 | writeJson(jsonWriter, configs); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 530 | } catch (IOException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 531 | Slog.e(TAG, "Could not persist config", e); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 532 | stagedFile.failWrite(fos); |
| 533 | return false; |
| 534 | } |
| 535 | stagedFile.finishWrite(fos); |
| 536 | return true; |
| 537 | } |
| 538 | |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 539 | @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, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 553 | @Nullable |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 554 | private List<CarUxRestrictionsConfiguration> readPersistedConfig(File file) { |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 555 | if (!file.exists()) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 556 | Slog.e(TAG, "Could not find config file: " + file.getName()); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 557 | return null; |
| 558 | } |
| 559 | |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 560 | // 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, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 565 | AtomicFile configFile = new AtomicFile(file); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 566 | try (JsonReader reader = new JsonReader( |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 567 | new InputStreamReader(configFile.openRead(), StandardCharsets.UTF_8))) { |
| 568 | List<CarUxRestrictionsConfiguration> configs = new ArrayList<>(); |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 569 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 577 | Slog.e(TAG, "Unable to parse schema for version " + schemaVersion); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 578 | } |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 579 | |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 580 | return configs; |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 581 | } catch (IOException e) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 582 | Slog.e(TAG, "Could not read persisted config file " + file.getName(), e); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 583 | } |
| 584 | return null; |
| 585 | } |
| 586 | |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 587 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 634 | Slog.e(TAG, "Could not read persisted config file " + file.getName(), e); |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 635 | } |
| 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 Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 649 | /** |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 650 | * 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 Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 654 | */ |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 655 | public void setUxRChangeBroadcastEnabled(boolean enable) { |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 656 | if (!isDebugBuild()) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 657 | Slog.e(TAG, "Cannot set UX restriction change broadcast."); |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 658 | 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 Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 667 | |
| 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 Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 681 | } |
| 682 | } |
| 683 | |
| 684 | private boolean isDebugBuild() { |
| 685 | return Build.IS_USERDEBUG || Build.IS_ENG; |
| 686 | } |
| 687 | |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 688 | @Override |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 689 | public void dump(IndentingPrintWriter writer) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 690 | synchronized (mLock) { |
| 691 | writer.println("*CarUxRestrictionsManagerService*"); |
| 692 | mUxRClients.dump(writer, "UX Restrictions Clients "); |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 693 | for (int port : mCurrentUxRestrictions.keySet()) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 694 | 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 Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 715 | } |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 716 | } |
| 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, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 726 | logd("Driving State Changed:" + event.eventValue); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 727 | synchronized (mLock) { |
| 728 | handleDrivingStateEventLocked(event); |
| 729 | } |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 730 | } |
| 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 Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 738 | @VisibleForTesting |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 739 | @GuardedBy("mLock") |
| 740 | void handleDrivingStateEventLocked(CarDrivingStateEvent event) { |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 741 | if (event == null) { |
| 742 | return; |
| 743 | } |
| 744 | int drivingState = event.eventValue; |
Ram Periathiruvadi | f6381fb | 2018-06-05 18:44:19 -0700 | [diff] [blame] | 745 | Float speed = getCurrentSpeed(); |
| 746 | |
| 747 | if (speed != SPEED_NOT_AVAILABLE) { |
| 748 | mCurrentMovingSpeed = speed; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 749 | } else if (drivingState == DRIVING_STATE_PARKED |
| 750 | || drivingState == DRIVING_STATE_UNKNOWN) { |
Ram Periathiruvadi | ec551ae | 2018-03-15 11:38:07 -0700 | [diff] [blame] | 751 | // If speed is unavailable, but the driving state is parked or unknown, it can still be |
| 752 | // handled. |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 753 | logd("Speed null when driving state is: " + drivingState); |
Ram Periathiruvadi | ec551ae | 2018-03-15 11:38:07 -0700 | [diff] [blame] | 754 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 759 | Slog.e(TAG, "Unexpected: Speed null when driving state is: " + drivingState); |
Ram Periathiruvadi | ec551ae | 2018-03-15 11:38:07 -0700 | [diff] [blame] | 760 | return; |
| 761 | } |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 762 | handleDispatchUxRestrictionsLocked(drivingState, mCurrentMovingSpeed); |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 763 | } |
| 764 | |
| 765 | /** |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 766 | * {@link CarPropertyEvent} listener registered with the {@link CarPropertyService} for getting |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 767 | * speed change notifications. |
| 768 | */ |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 769 | private final ICarPropertyEventListener mICarPropertyEventListener = |
| 770 | new ICarPropertyEventListener.Stub() { |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 771 | @Override |
Steve Paik | 4d25702 | 2018-04-27 13:28:31 -0700 | [diff] [blame] | 772 | public void onEvent(List<CarPropertyEvent> events) throws RemoteException { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 773 | 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 Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 782 | } |
| 783 | } |
| 784 | } |
| 785 | }; |
| 786 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 787 | @GuardedBy("mLock") |
| 788 | private void handleSpeedChangeLocked(float newSpeed) { |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 789 | if (newSpeed == mCurrentMovingSpeed) { |
| 790 | // Ignore if speed hasn't changed |
| 791 | return; |
| 792 | } |
| 793 | int currentDrivingState = mDrivingStateService.getCurrentDrivingState().eventValue; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 794 | if (currentDrivingState != DRIVING_STATE_MOVING) { |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 795 | // Ignore speed changes if the vehicle is not moving |
| 796 | return; |
| 797 | } |
| 798 | mCurrentMovingSpeed = newSpeed; |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 799 | handleDispatchUxRestrictionsLocked(currentDrivingState, newSpeed); |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 800 | } |
| 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 Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 808 | @GuardedBy("mLock") |
| 809 | private void handleDispatchUxRestrictionsLocked(@CarDrivingState int currentDrivingState, |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 810 | float speed) { |
Daulet Zhanguzin | 4ded678 | 2020-01-03 15:47:10 +0000 | [diff] [blame] | 811 | Objects.requireNonNull(mCarUxRestrictionsConfigurations, |
Jordan Jozwiak | ef17299 | 2019-07-31 17:53:12 -0700 | [diff] [blame] | 812 | "mCarUxRestrictionsConfigurations must be initialized"); |
Daulet Zhanguzin | 4ded678 | 2020-01-03 15:47:10 +0000 | [diff] [blame] | 813 | Objects.requireNonNull(mCurrentUxRestrictions, |
Jordan Jozwiak | ef17299 | 2019-07-31 17:53:12 -0700 | [diff] [blame] | 814 | "mCurrentUxRestrictions must be initialized"); |
| 815 | |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 816 | if (isDebugBuild() && !mUxRChangeBroadcastEnabled) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 817 | Slog.d(TAG, "Not dispatching UX Restriction due to setting"); |
Ram Periathiruvadi | cdee120 | 2018-07-17 18:01:31 -0700 | [diff] [blame] | 818 | return; |
| 819 | } |
| 820 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 821 | Map<Integer, CarUxRestrictions> newUxRestrictions = new HashMap<>(); |
| 822 | for (int port : mPhysicalPorts) { |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 823 | CarUxRestrictionsConfiguration config = mCarUxRestrictionsConfigurations.get(port); |
Keun young Park | 4aa19c2 | 2019-05-03 18:25:01 -0700 | [diff] [blame] | 824 | if (config == null) { |
| 825 | continue; |
| 826 | } |
Ram Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 827 | |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 828 | 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 Periathiruvadi | 12f4243 | 2018-03-28 19:47:37 -0700 | [diff] [blame] | 833 | uxRestrictions.isRequiresDistractionOptimization())); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 834 | logd(String.format("Display port 0x%02x\tUxR old->new: 0x%x -> 0x%x", |
| 835 | port, |
| 836 | mCurrentUxRestrictions.get(port).getActiveRestrictions(), |
Ram Periathiruvadi | 12f4243 | 2018-03-28 19:47:37 -0700 | [diff] [blame] | 837 | uxRestrictions.getActiveRestrictions())); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 838 | newUxRestrictions.put(port, uxRestrictions); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 839 | } |
Ram Periathiruvadi | 4526a43 | 2018-01-24 13:00:54 -0800 | [diff] [blame] | 840 | |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 841 | // Ignore dispatching if the restrictions has not changed. |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 842 | Set<Integer> displayToDispatch = new ArraySet<>(); |
| 843 | for (int port : newUxRestrictions.keySet()) { |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 844 | if (!mCurrentUxRestrictions.containsKey(port)) { |
| 845 | // This should never happen. |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 846 | Slog.wtf(TAG, "Unrecognized port:" + port); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 847 | 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 Periathiruvadi | ed9e8f8 | 2018-03-07 11:50:49 -0800 | [diff] [blame] | 855 | return; |
| 856 | } |
Ram Periathiruvadi | 22c0d68 | 2018-05-10 17:50:48 -0700 | [diff] [blame] | 857 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 858 | for (int port : displayToDispatch) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 859 | addTransitionLogLocked( |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 860 | mCurrentUxRestrictions.get(port), newUxRestrictions.get(port)); |
| 861 | } |
| 862 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 863 | dispatchRestrictionsToClients(newUxRestrictions, displayToDispatch); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 864 | |
| 865 | mCurrentUxRestrictions = newUxRestrictions; |
| 866 | } |
| 867 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 868 | private void dispatchRestrictionsToClients(Map<Integer, CarUxRestrictions> displayRestrictions, |
| 869 | Set<Integer> displayToDispatch) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 870 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 888 | Slog.e(TAG, |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 889 | String.format("Dispatch to listener %s failed for restrictions (%s)", |
| 890 | callback, restrictions)); |
| 891 | } |
| 892 | } |
| 893 | mUxRClients.finishBroadcast(); |
| 894 | }); |
| 895 | |
| 896 | if (!success) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 897 | Slog.e(TAG, "Unable to post (" + displayRestrictions + ") event to dispatch handler"); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 898 | } |
| 899 | } |
| 900 | |
Jordan Jozwiak | e1a8459 | 2020-02-28 12:55:30 -0800 | [diff] [blame] | 901 | @VisibleForTesting |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 902 | static int getDefaultDisplayPhysicalPort(DisplayManager displayManager) { |
Jordan Jozwiak | e1a8459 | 2020-02-28 12:55:30 -0800 | [diff] [blame] | 903 | Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 904 | DisplayAddress.Physical address = (DisplayAddress.Physical) defaultDisplay.getAddress(); |
| 905 | |
| 906 | if (address == null) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 907 | Slog.e(TAG, "Default display does not have physical display port. Using 0 as port."); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 908 | 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 Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 923 | Slog.i(TAG, "Default display does not have display address. Using default."); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 924 | } |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 925 | synchronized (mLock) { |
| 926 | mPhysicalPorts.add(mDefaultDisplayPhysicalPort); |
| 927 | } |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 928 | } else if (display.getAddress() instanceof DisplayAddress.Physical) { |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 929 | int port = ((DisplayAddress.Physical) display.getAddress()).getPort(); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 930 | if (Log.isLoggable(TAG, Log.INFO)) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 931 | Slog.i(TAG, "Display " + display.getDisplayId() + " uses port " + port); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 932 | } |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 933 | synchronized (mLock) { |
| 934 | mPhysicalPorts.add(port); |
| 935 | } |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 936 | } else { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 937 | Slog.w(TAG, "At init non-virtual display has a non-physical display address: " |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 938 | + display); |
| 939 | } |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 940 | } |
| 941 | } |
| 942 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 943 | private Map<Integer, CarUxRestrictionsConfiguration> convertToMap( |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 944 | List<CarUxRestrictionsConfiguration> configs) { |
| 945 | validateConfigs(configs); |
| 946 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 947 | Map<Integer, CarUxRestrictionsConfiguration> result = new HashMap<>(); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 948 | if (configs.size() == 1) { |
| 949 | CarUxRestrictionsConfiguration config = configs.get(0); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 950 | synchronized (mLock) { |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 951 | int port = config.getPhysicalPort() == null |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 952 | ? mDefaultDisplayPhysicalPort |
| 953 | : config.getPhysicalPort(); |
| 954 | result.put(port, config); |
| 955 | } |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 956 | } 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 Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 981 | Set<Integer> existingPorts = new ArraySet<>(); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 982 | 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 Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 991 | Integer port = config.getPhysicalPort(); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 992 | 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 Jozwiak | b711ace | 2019-05-21 14:24:23 -0700 | [diff] [blame] | 1005 | /** |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 1006 | * Returns the physical port id for the display or {@code null} if {@link |
Jordan Jozwiak | b711ace | 2019-05-21 14:24:23 -0700 | [diff] [blame] | 1007 | * DisplayManager#getDisplay(int)} is not aware of the provided id. |
| 1008 | */ |
| 1009 | @Nullable |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1010 | @GuardedBy("mLock") |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 1011 | private Integer getPhysicalPortLocked(int displayId) { |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1012 | if (!mPortLookup.containsKey(displayId)) { |
| 1013 | Display display = mDisplayManager.getDisplay(displayId); |
| 1014 | if (display == null) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 1015 | Slog.w(TAG, "Could not retrieve display for id: " + displayId); |
Jordan Jozwiak | b711ace | 2019-05-21 14:24:23 -0700 | [diff] [blame] | 1016 | return null; |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1017 | } |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 1018 | int port = doGetPhysicalPortLocked(display); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1019 | mPortLookup.put(displayId, port); |
| 1020 | } |
| 1021 | return mPortLookup.get(displayId); |
| 1022 | } |
| 1023 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1024 | @GuardedBy("mLock") |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 1025 | private int doGetPhysicalPortLocked(@NonNull Display display) { |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1026 | if (display.getType() == Display.TYPE_VIRTUAL) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 1027 | Slog.e(TAG, "Display " + display |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 1028 | + " is a virtual display and does not have a known port."); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1029 | return mDefaultDisplayPhysicalPort; |
| 1030 | } |
| 1031 | |
| 1032 | DisplayAddress address = display.getAddress(); |
| 1033 | if (address == null) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 1034 | Slog.e(TAG, "Display " + display |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1035 | + " is not a virtual display but has null DisplayAddress."); |
| 1036 | return mDefaultDisplayPhysicalPort; |
| 1037 | } else if (!(address instanceof DisplayAddress.Physical)) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 1038 | Slog.e(TAG, "Display " + display + " has non-physical address: " + address); |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1039 | 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 Jozwiak | b711ace | 2019-05-21 14:24:23 -0700 | [diff] [blame] | 1051 | private CarUxRestrictions createFullyRestrictedRestrictions() { |
| 1052 | return new CarUxRestrictions.Builder( |
| 1053 | /*reqOpt= */ true, |
| 1054 | CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED, |
| 1055 | SystemClock.elapsedRealtimeNanos()).build(); |
| 1056 | } |
| 1057 | |
Marin Shalamanov | e85bd56 | 2020-04-24 17:21:32 +0200 | [diff] [blame] | 1058 | CarUxRestrictionsConfiguration createDefaultConfig(int port) { |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 1059 | return new CarUxRestrictionsConfiguration.Builder() |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1060 | .setPhysicalPort(port) |
| 1061 | .setUxRestrictions(DRIVING_STATE_PARKED, |
Wenting Zhai | 2902131 | 2019-01-09 12:11:14 -0800 | [diff] [blame] | 1062 | false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE) |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1063 | .setUxRestrictions(DRIVING_STATE_IDLING, |
Wenting Zhai | 2902131 | 2019-01-09 12:11:14 -0800 | [diff] [blame] | 1064 | false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE) |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1065 | .setUxRestrictions(DRIVING_STATE_MOVING, |
Wenting Zhai | 2902131 | 2019-01-09 12:11:14 -0800 | [diff] [blame] | 1066 | true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED) |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1067 | .setUxRestrictions(DRIVING_STATE_UNKNOWN, |
Wenting Zhai | 2902131 | 2019-01-09 12:11:14 -0800 | [diff] [blame] | 1068 | true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED) |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 1069 | .build(); |
Ram Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 1070 | } |
Ram Periathiruvadi | 22c0d68 | 2018-05-10 17:50:48 -0700 | [diff] [blame] | 1071 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1072 | @GuardedBy("mLock") |
| 1073 | private void addTransitionLogLocked(String name, String from, String to, long timestamp, |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 1074 | String extra) { |
Ram Periathiruvadi | 22c0d68 | 2018-05-10 17:50:48 -0700 | [diff] [blame] | 1075 | 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 Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1083 | @GuardedBy("mLock") |
| 1084 | private void addTransitionLogLocked( |
Yao, Yuxing | c8bd72d | 2019-03-12 14:52:48 -0700 | [diff] [blame] | 1085 | 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, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 1099 | private static void logd(String msg) { |
| 1100 | if (DBG) { |
Yao, Yuxing | 9bfb749 | 2019-02-15 11:53:34 -0800 | [diff] [blame] | 1101 | Slog.d(TAG, msg); |
Yao, Yuxing | 0811d5a | 2018-11-29 16:18:53 -0800 | [diff] [blame] | 1102 | } |
| 1103 | } |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1104 | |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1105 | private static final class DisplayInfo { |
| 1106 | final IRemoteCallback mOwner; |
| 1107 | final int mPhysicalDisplayId; |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 1108 | |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1109 | DisplayInfo(IRemoteCallback owner, int physicalDisplayId) { |
| 1110 | mOwner = owner; |
| 1111 | mPhysicalDisplayId = physicalDisplayId; |
| 1112 | } |
Jordan Jozwiak | 0b0cf28 | 2020-01-17 12:26:42 -0800 | [diff] [blame] | 1113 | } |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1114 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1115 | @GuardedBy("mLock") |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1116 | private final SparseArray<DisplayInfo> mActivityViewDisplayInfoMap = new SparseArray<>(); |
| 1117 | |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1118 | @GuardedBy("mLock") |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1119 | private final RemoteCallbackList<IRemoteCallback> mRemoteCallbackList = |
| 1120 | new RemoteCallbackList<>() { |
| 1121 | @Override |
| 1122 | public void onCallbackDied(IRemoteCallback callback) { |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1123 | synchronized (mLock) { |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1124 | // 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 Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 1130 | mPortLookup.remove(mActivityViewDisplayInfoMap.keyAt(i)); |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1131 | } |
| 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 Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1145 | synchronized (mLock) { |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1146 | if (release) { |
| 1147 | mRemoteCallbackList.unregister(callback); |
| 1148 | mActivityViewDisplayInfoMap.delete(virtualDisplayId); |
Jordan Jozwiak | 5465ca9 | 2020-02-03 12:04:12 -0800 | [diff] [blame] | 1149 | mPortLookup.remove(virtualDisplayId); |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1150 | return; |
| 1151 | } |
| 1152 | mRemoteCallbackList.register(callback); |
| 1153 | mActivityViewDisplayInfoMap.put(virtualDisplayId, |
| 1154 | new DisplayInfo(callback, physicalDisplayId)); |
Jordan Jozwiak | 7c7afc6 | 2020-06-12 18:01:40 -0700 | [diff] [blame] | 1155 | Integer physicalPort = getPhysicalPortLocked(physicalDisplayId); |
| 1156 | if (physicalPort == null) { |
| 1157 | // This should not happen. |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 1158 | Slog.wtf(TAG, "No known physicalPort for displayId:" + physicalDisplayId); |
Jordan Jozwiak | 7c7afc6 | 2020-06-12 18:01:40 -0700 | [diff] [blame] | 1159 | physicalPort = mDefaultDisplayPhysicalPort; |
| 1160 | } |
| 1161 | mPortLookup.put(virtualDisplayId, physicalPort); |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1162 | } |
| 1163 | } |
| 1164 | |
| 1165 | @Override |
| 1166 | public int getMappedPhysicalDisplayOfVirtualDisplay(int displayId) { |
| 1167 | logd("getMappedPhysicalDisplayOfVirtualDisplay: displayId=" + displayId); |
Jordan Jozwiak | 576578a | 2020-02-27 17:06:24 -0800 | [diff] [blame] | 1168 | synchronized (mLock) { |
Yuncheol Heo | b9554e1 | 2019-10-08 15:51:49 -0700 | [diff] [blame] | 1169 | 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 Periathiruvadi | 25c16f1 | 2017-11-17 16:48:37 -0800 | [diff] [blame] | 1201 | } |