summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBill Yi2018-11-30 13:50:08 -0600
committerandroid-build-merger2018-11-30 13:50:08 -0600
commited10dc727d239fa7857390a3787edc2c99e9339e (patch)
treed7b300e81f05e45345ada1ffcaf39b2512dd8f6f
parentf02b56678700a4035d0ad8882f7d20371bb96ee2 (diff)
parent0f8320f6b95736ea4b703764a7d11a8a6ca13674 (diff)
downloadplatform-packages-services-car-pie-gsi.tar.gz
platform-packages-services-car-pie-gsi.tar.xz
platform-packages-services-car-pie-gsi.zip
Merge pi-qpr1-release PQ1A.181105.017.A1 to pi-platform-releasepie-gsi
am: 0f8320f6b9 Change-Id: I7f71b2eff90d4f6088640305f64f25b4f166466f
-rw-r--r--.gitignore1
-rw-r--r--TrustAgent/res/values/strings.xml2
-rw-r--r--TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java191
-rw-r--r--TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java68
-rw-r--r--TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java22
-rw-r--r--car-lib/api/current.txt124
-rw-r--r--car-lib/api/system-current.txt8
-rw-r--r--car-lib/src/android/car/Car.java8
-rw-r--r--car-lib/src/android/car/CarInfoManager.java56
-rw-r--r--car-lib/src/android/car/PortLocationType.java35
-rw-r--r--car-lib/src/android/car/VehicleLightState.java31
-rw-r--r--car-lib/src/android/car/VehicleLightSwitch.java31
-rw-r--r--car-lib/src/android/car/VehiclePropertyAccess.java35
-rw-r--r--car-lib/src/android/car/VehiclePropertyIds.java888
-rw-r--r--car-lib/src/android/car/drivingstate/CarUxRestrictions.java32
-rw-r--r--car-lib/src/android/car/hardware/CarSensorEvent.java43
-rw-r--r--car-lib/src/android/car/hardware/CarSensorManager.java15
-rw-r--r--car-lib/src/android/car/hardware/property/CarPropertyManager.java83
-rw-r--r--car-lib/src/android/car/media/CarAudioManager.java7
-rw-r--r--car-lib/src/android/car/settings/CarSettings.java30
-rw-r--r--car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl3
-rw-r--r--car-lib/src/android/car/user/CarUserManagerHelper.java403
-rw-r--r--car-support-lib/proguard-release.flags8
-rw-r--r--car_product/overlay/frameworks/base/core/res/res/values/config.xml2
-rw-r--r--car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml85
-rw-r--r--car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pin_view.xml104
-rw-r--r--car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml2
-rw-r--r--car_product/sepolicy/private/carservice_app.te2
-rw-r--r--evs/app/RenderDirectView.cpp3
-rw-r--r--evs/app/RenderTopView.cpp3
-rw-r--r--evs/sampleDriver/GlWrapper.cpp5
-rw-r--r--evs/sampleDriver/ServiceNames.h4
-rw-r--r--evs/sampleDriver/VideoCapture.h4
-rw-r--r--service/Android.mk6
-rw-r--r--service/AndroidManifest.xml5
-rw-r--r--service/res/values/config.xml6
-rw-r--r--service/src/com/android/car/CarAudioService.java44
-rw-r--r--service/src/com/android/car/CarInputService.java42
-rw-r--r--service/src/com/android/car/CarLocationService.java188
-rw-r--r--service/src/com/android/car/CarNightService.java39
-rw-r--r--service/src/com/android/car/CarPropertyService.java23
-rw-r--r--service/src/com/android/car/CarUxRestrictionsManagerService.java55
-rw-r--r--service/src/com/android/car/CarUxRestrictionsServiceHelper.java4
-rw-r--r--service/src/com/android/car/ICarImpl.java7
-rw-r--r--service/src/com/android/car/garagemode/GarageModePolicy.java157
-rw-r--r--service/src/com/android/car/garagemode/GarageModeService.java (renamed from service/src/com/android/car/GarageModeService.java)180
-rw-r--r--service/src/com/android/car/garagemode/WakeupInterval.java48
-rw-r--r--service/src/com/android/car/hal/CarPropertyUtils.java36
-rw-r--r--service/src/com/android/car/hal/PropertyHalServiceIds.java3
-rw-r--r--service/src/com/android/car/pm/CarPackageManagerService.java62
-rw-r--r--service/src/com/android/car/systeminterface/DisplayInterface.java44
-rw-r--r--service/src/com/android/car/user/CarUserService.java38
-rw-r--r--tests/CarTrustAgentClientApp/Android.mk15
-rw-r--r--tests/CarTrustAgentClientApp/AndroidManifest.xml34
-rw-r--r--tests/CarTrustAgentClientApp/README.txt2
-rw-r--r--tests/CarTrustAgentClientApp/res/layout/phone_enrolment_activity.xml54
-rw-r--r--tests/CarTrustAgentClientApp/res/values/strings.xml22
-rw-r--r--tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentActivity.java61
-rw-r--r--tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentController.java183
-rw-r--r--tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneUnlockController.java149
-rw-r--r--tests/CarTrustAgentClientApp/src/com/android/car/trust/client/SimpleBleClient.java355
-rw-r--r--tests/CarTrustAgentClientApp/src/com/android/car/trust/client/Utils.java46
-rw-r--r--tests/EmbeddedKitchenSinkApp/AndroidManifest.xml2
-rw-r--r--tests/EmbeddedKitchenSinkApp/res/layout/connectivity_fragment.xml55
-rw-r--r--tests/EmbeddedKitchenSinkApp/res/layout/list_item.xml22
-rw-r--r--tests/EmbeddedKitchenSinkApp/res/values/strings.xml5
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java70
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/connectivity/ConnectivityFragment.java154
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java141
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/power/PowerTestFragment.java23
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java32
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java21
-rw-r--r--tests/carservice_test/AndroidManifest.xml5
-rw-r--r--tests/carservice_test/src/com/android/car/MockedCarTestBase.java4
-rw-r--r--tests/carservice_test/src/com/android/car/garagemode/GarageModeServiceTest.java (renamed from tests/carservice_test/src/com/android/car/GarageModeTest.java)143
-rw-r--r--tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java52
-rw-r--r--tests/carservice_unit_test/src/com/android/car/CarUserManagerHelperTest.java655
-rw-r--r--tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java136
-rw-r--r--tests/robotests/src/com/android/car/users/CarUserManagerHelperRoboTest.java2
79 files changed, 4817 insertions, 951 deletions
diff --git a/.gitignore b/.gitignore
index b2928471..697ef2da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@
5gen/ 5gen/
6*.pyc 6*.pyc
7__pycache__ 7__pycache__
8.idea \ No newline at end of file
diff --git a/TrustAgent/res/values/strings.xml b/TrustAgent/res/values/strings.xml
index d0772621..192b7e4b 100644
--- a/TrustAgent/res/values/strings.xml
+++ b/TrustAgent/res/values/strings.xml
@@ -36,4 +36,6 @@
36 <string name="start_advertising">Start Advertising</string> 36 <string name="start_advertising">Start Advertising</string>
37 <string name="revoke_trust">Revoke Trust</string> 37 <string name="revoke_trust">Revoke Trust</string>
38 38
39 <string translatable="false" name="token_handle_shared_preferences">com.android.car.trust.TOKEN_HANDLE</string>
40
39</resources> 41</resources>
diff --git a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
index 227ea891..1dfbd504 100644
--- a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
+++ b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
@@ -16,27 +16,25 @@
16 16
17package com.android.car.trust; 17package com.android.car.trust;
18 18
19import android.bluetooth.BluetoothDevice; 19import android.annotation.Nullable;
20import android.bluetooth.BluetoothGattServer; 20import android.app.ActivityManager;
21import android.bluetooth.BluetoothGattServerCallback; 21import android.app.UserSwitchObserver;
22import android.bluetooth.BluetoothManager; 22import android.bluetooth.BluetoothAdapter;
23import android.car.trust.ICarTrustAgentBleService; 23import android.car.trust.ICarTrustAgentBleService;
24import android.car.trust.ICarTrustAgentTokenRequestDelegate; 24import android.car.trust.ICarTrustAgentTokenRequestDelegate;
25import android.car.trust.ICarTrustAgentUnlockCallback; 25import android.car.trust.ICarTrustAgentUnlockCallback;
26import android.content.BroadcastReceiver;
26import android.content.ComponentName; 27import android.content.ComponentName;
27import android.content.Context; 28import android.content.Context;
28import android.content.Intent; 29import android.content.Intent;
30import android.content.IntentFilter;
29import android.content.ServiceConnection; 31import android.content.ServiceConnection;
30import android.os.Handler;
31import android.os.IBinder; 32import android.os.IBinder;
32import android.os.RemoteException; 33import android.os.RemoteException;
33import android.os.UserHandle; 34import android.os.UserHandle;
34import android.os.UserManager;
35import android.service.trust.TrustAgentService; 35import android.service.trust.TrustAgentService;
36import android.util.Log; 36import android.util.Log;
37 37
38import java.util.concurrent.TimeUnit;
39
40/** 38/**
41 * A BluetoothLE (BLE) based {@link TrustAgentService} that uses the escrow token unlock APIs. </p> 39 * A BluetoothLE (BLE) based {@link TrustAgentService} that uses the escrow token unlock APIs. </p>
42 * 40 *
@@ -49,39 +47,44 @@ public class CarBleTrustAgent extends TrustAgentService {
49 47
50 private static final String TAG = CarBleTrustAgent.class.getSimpleName(); 48 private static final String TAG = CarBleTrustAgent.class.getSimpleName();
51 49
52 private static final long TRUST_DURATION_MS = TimeUnit.MINUTES.toMicros(5); 50 /**
53 private static final long BLE_RETRY_MS = TimeUnit.SECONDS.toMillis(1); 51 * {@link CarTrustAgentBleService} will callback this function when it receives both
54 52 * handle and token.
55 private Handler mHandler; 53 */
56 private BluetoothManager mBluetoothManager;
57 private ICarTrustAgentBleService mCarTrustAgentBleService;
58 private boolean mCarTrustAgentBleServiceBound;
59
60 private final ICarTrustAgentUnlockCallback mUnlockCallback = 54 private final ICarTrustAgentUnlockCallback mUnlockCallback =
61 new ICarTrustAgentUnlockCallback.Stub() { 55 new ICarTrustAgentUnlockCallback.Stub() {
62 @Override 56 @Override
63 public void onUnlockDataReceived(byte[] token, long handle) { 57 public void onUnlockDataReceived(byte[] token, long handle) throws RemoteException {
64 UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 58 UserHandle userHandle = getUserHandleByTokenHandle(handle);
65 // TODO(b/77854782): get the actual user to unlock by token 59 if (userHandle == null) {
66 UserHandle userHandle = getForegroundUserHandle(); 60 Log.e(TAG, "Unable to find user by token handle " + handle);
67 61 return;
68 Log.d(TAG, "About to unlock user. Handle: " + handle 62 }
69 + " Time: " + System.currentTimeMillis());
70 unlockUserWithToken(handle, token, userHandle);
71
72 Log.d(TAG, "Attempted to unlock user, is user unlocked: "
73 + um.isUserUnlocked(userHandle)
74 + " Time: " + System.currentTimeMillis());
75 setManagingTrust(true);
76 63
77 if (um.isUserUnlocked(userHandle)) { 64 int uid = userHandle.getIdentifier();
78 Log.d(TAG, getString(R.string.trust_granted_explanation)); 65 if (ActivityManager.getCurrentUser() != uid) {
79 grantTrust("Granting trust from escrow token", 66 Log.d(TAG, "Switch to user: " + uid);
80 TRUST_DURATION_MS, FLAG_GRANT_TRUST_DISMISS_KEYGUARD); 67 // Try to unlock when user switch completes
68 ActivityManager.getService().registerUserSwitchObserver(
69 getUserSwitchObserver(uid, token, handle), TAG);
70 ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
71 am.switchUser(uid);
72 } else {
73 unlockUserInternally(uid, token, handle);
81 } 74 }
82 } 75 }
83 }; 76 };
84 77
78 /**
79 * Delegates the escrow token API calls from {@link CarTrustAgentBleService} to
80 * {@link TrustAgentService}. Due to the asynchronous nature, the results will be posted to
81 * {@link CarTrustAgentBleService} by the following calls
82 * <ul>
83 * <li>{@link #onEscrowTokenAdded(byte[], long, UserHandle)}</li>
84 * <li>{@link #onEscrowTokenRemoved(long, boolean)}</li>
85 * <li>{@link #onEscrowTokenStateReceived(long, int)}</li>
86 * </ul>
87 */
85 private final ICarTrustAgentTokenRequestDelegate mTokenRequestDelegate = 88 private final ICarTrustAgentTokenRequestDelegate mTokenRequestDelegate =
86 new ICarTrustAgentTokenRequestDelegate.Stub() { 89 new ICarTrustAgentTokenRequestDelegate.Stub() {
87 @Override 90 @Override
@@ -105,6 +108,9 @@ public class CarBleTrustAgent extends TrustAgentService {
105 } 108 }
106 }; 109 };
107 110
111 /**
112 * Service connection to {@link CarTrustAgentBleService}
113 */
108 private final ServiceConnection mServiceConnection = new ServiceConnection() { 114 private final ServiceConnection mServiceConnection = new ServiceConnection() {
109 @Override 115 @Override
110 public void onServiceConnected(ComponentName name, IBinder service) { 116 public void onServiceConnected(ComponentName name, IBinder service) {
@@ -114,7 +120,6 @@ public class CarBleTrustAgent extends TrustAgentService {
114 try { 120 try {
115 mCarTrustAgentBleService.registerUnlockCallback(mUnlockCallback); 121 mCarTrustAgentBleService.registerUnlockCallback(mUnlockCallback);
116 mCarTrustAgentBleService.setTokenRequestDelegate(mTokenRequestDelegate); 122 mCarTrustAgentBleService.setTokenRequestDelegate(mTokenRequestDelegate);
117 maybeStartBleUnlockService();
118 } catch (RemoteException e) { 123 } catch (RemoteException e) {
119 Log.e(TAG, "Error registerUnlockCallback", e); 124 Log.e(TAG, "Error registerUnlockCallback", e);
120 } 125 }
@@ -122,10 +127,12 @@ public class CarBleTrustAgent extends TrustAgentService {
122 127
123 @Override 128 @Override
124 public void onServiceDisconnected(ComponentName name) { 129 public void onServiceDisconnected(ComponentName name) {
130 Log.d(TAG, "CarTrustAgentBleService disconnected");
125 if (mCarTrustAgentBleService != null) { 131 if (mCarTrustAgentBleService != null) {
126 try { 132 try {
127 mCarTrustAgentBleService.unregisterUnlockCallback(mUnlockCallback); 133 mCarTrustAgentBleService.unregisterUnlockCallback(mUnlockCallback);
128 mCarTrustAgentBleService.setTokenRequestDelegate(null); 134 mCarTrustAgentBleService.setTokenRequestDelegate(null);
135 mCarTrustAgentBleService.stopUnlockAdvertising();
129 } catch (RemoteException e) { 136 } catch (RemoteException e) {
130 Log.e(TAG, "Error unregisterUnlockCallback", e); 137 Log.e(TAG, "Error unregisterUnlockCallback", e);
131 } 138 }
@@ -135,45 +142,57 @@ public class CarBleTrustAgent extends TrustAgentService {
135 } 142 }
136 }; 143 };
137 144
145 /**
146 * Receives the bluetooth state change broadcasts. Bluetooth is restarted when switching user,
147 * we need to ensure calling {@link ICarTrustAgentBleService#startUnlockAdvertising} after
148 * bluetooth is started.
149 */
150 private final BroadcastReceiver mBluetoothBroadcastReceiver = new BroadcastReceiver() {
151 @Override
152 public void onReceive(Context context, Intent intent) {
153 switch (intent.getAction()) {
154 case BluetoothAdapter.ACTION_STATE_CHANGED:
155 onBluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1));
156 break;
157 }
158 }
159 };
160
161 private ICarTrustAgentBleService mCarTrustAgentBleService;
162 private boolean mCarTrustAgentBleServiceBound;
163
164 /**
165 * TODO: Currently it relies on {@link #onDeviceLocked()} and {@link #onDeviceUnlocked()}
166 * callback, and these callbacks won't happen if the user has unlocked once.
167 */
168 private boolean mIsOnLockScreen;
169
138 @Override 170 @Override
139 public void onCreate() { 171 public void onCreate() {
140 super.onCreate(); 172 super.onCreate();
141 173 setManagingTrust(true);
142 Log.d(TAG, "Bluetooth trust agent starting up"); 174 bindService(new Intent(this, CarTrustAgentBleService.class),
143 mHandler = new Handler(); 175 mServiceConnection, Context.BIND_AUTO_CREATE);
144 mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); 176 IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
145 177 registerReceiver(mBluetoothBroadcastReceiver, intentFilter);
146 // If the user is already unlocked, don't bother starting the BLE service.
147 UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
148 if (!um.isUserUnlocked(getForegroundUserHandle())) {
149 Log.d(TAG, "User locked, will now bind CarTrustAgentBleService");
150 Intent intent = new Intent(this, CarTrustAgentBleService.class);
151 bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
152 } else {
153 setManagingTrust(true);
154 }
155 } 178 }
156 179
157 @Override 180 @Override
158 public void onDestroy() { 181 public void onDestroy() {
159 Log.d(TAG, "Car Trust agent shutting down"); 182 Log.d(TAG, "Car Trust agent shutting down");
160 mHandler.removeCallbacks(null);
161
162 // Unbind the service to avoid leaks from BLE stack.
163 if (mCarTrustAgentBleServiceBound) { 183 if (mCarTrustAgentBleServiceBound) {
164 unbindService(mServiceConnection); 184 unbindService(mServiceConnection);
165 } 185 }
186 unregisterReceiver(mBluetoothBroadcastReceiver);
166 super.onDestroy(); 187 super.onDestroy();
167 } 188 }
168 189
169 @Override 190 @Override
170 public void onDeviceLocked() { 191 public void onDeviceLocked() {
171 super.onDeviceLocked(); 192 super.onDeviceLocked();
193 mIsOnLockScreen = true;
172 if (mCarTrustAgentBleServiceBound) { 194 if (mCarTrustAgentBleServiceBound) {
173 try { 195 try {
174 // Only one BLE advertising is allowed, ensure enrolment advertising is stopped
175 // before start unlock advertising.
176 mCarTrustAgentBleService.stopEnrolmentAdvertising();
177 mCarTrustAgentBleService.startUnlockAdvertising(); 196 mCarTrustAgentBleService.startUnlockAdvertising();
178 } catch (RemoteException e) { 197 } catch (RemoteException e) {
179 Log.e(TAG, "Error startUnlockAdvertising", e); 198 Log.e(TAG, "Error startUnlockAdvertising", e);
@@ -184,36 +203,47 @@ public class CarBleTrustAgent extends TrustAgentService {
184 @Override 203 @Override
185 public void onDeviceUnlocked() { 204 public void onDeviceUnlocked() {
186 super.onDeviceUnlocked(); 205 super.onDeviceUnlocked();
206 mIsOnLockScreen = false;
187 if (mCarTrustAgentBleServiceBound) { 207 if (mCarTrustAgentBleServiceBound) {
188 try { 208 try {
189 // Only one BLE advertising is allowed, ensure unlock advertising is stopped.
190 mCarTrustAgentBleService.stopUnlockAdvertising(); 209 mCarTrustAgentBleService.stopUnlockAdvertising();
191 } catch (RemoteException e) { 210 } catch (RemoteException e) {
192 Log.e(TAG, "Error stopUnlockAdvertising", e); 211 Log.e(TAG, "Error stopUnlockAdvertising", e);
193 } 212 }
194 } 213 }
214 // Revoke trust right after to enable keyguard when switching user
215 revokeTrust();
195 } 216 }
196 217
197 private void maybeStartBleUnlockService() { 218 private UserSwitchObserver getUserSwitchObserver(int uid,
198 Log.d(TAG, "Trying to open a Ble GATT server"); 219 byte[] token, long handle) {
199 BluetoothGattServer gattServer = mBluetoothManager.openGattServer( 220 return new UserSwitchObserver() {
200 this, new BluetoothGattServerCallback() { 221 @Override
222 public void onUserSwitchComplete(int newUserId) throws RemoteException {
223 if (uid != newUserId) return;
224 unlockUserInternally(uid, token, handle);
225 ActivityManager.getService().unregisterUserSwitchObserver(this);
226 }
227
201 @Override 228 @Override
202 public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { 229 public void onLockedBootComplete(int newUserId) {
203 super.onConnectionStateChange(device, status, newState); 230 // ignored.
204 } 231 }
205 }); 232 };
233 }
234
235 private void unlockUserInternally(int uid, byte[] token, long handle) {
236 Log.d(TAG, "About to unlock user: " + uid);
237 unlockUserWithToken(handle, token, UserHandle.of(uid));
238 grantTrust("Granting trust from escrow token",
239 0, FLAG_GRANT_TRUST_DISMISS_KEYGUARD);
240 }
206 241
207 // The BLE stack is started up before the trust agent service, however Gatt capabilities 242 private void onBluetoothStateChanged(int state) {
208 // might not be ready just yet. Keep trying until a GattServer can open up before proceeding 243 Log.d(TAG, "onBluetoothStateChanged: " + state);
209 // to start the rest of the BLE services. 244 if ((state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON)
210 if (gattServer == null) { 245 && mCarTrustAgentBleServiceBound
211 Log.e(TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms"); 246 && mIsOnLockScreen) {
212 mHandler.postDelayed(this::maybeStartBleUnlockService, BLE_RETRY_MS);
213 } else {
214 mHandler.removeCallbacks(null);
215 gattServer.close();
216 Log.d(TAG, "GATT available, starting up UnlockService");
217 try { 247 try {
218 mCarTrustAgentBleService.startUnlockAdvertising(); 248 mCarTrustAgentBleService.startUnlockAdvertising();
219 } catch (RemoteException e) { 249 } catch (RemoteException e) {
@@ -259,12 +289,15 @@ public class CarBleTrustAgent extends TrustAgentService {
259 } 289 }
260 } 290 }
261 291
262 /** 292 private @Nullable UserHandle getUserHandleByTokenHandle(long tokenHandle) {
263 * TODO(b/77854782): return the {@link UserHandle} of foreground user. 293 if (mCarTrustAgentBleServiceBound) {
264 * CarBleTrustAgent itself runs as user-0 294 try {
265 */ 295 int userId = mCarTrustAgentBleService.getUserIdByEscrowTokenHandle(tokenHandle);
266 private UserHandle getForegroundUserHandle() { 296 return userId < 0 ? null : UserHandle.of(userId);
267 Log.d(TAG, "getForegroundUserHandle for " + UserHandle.myUserId()); 297 } catch (RemoteException e) {
268 return UserHandle.of(UserHandle.myUserId()); 298 Log.e(TAG, "Error getUserHandleByTokenHandle");
299 }
300 }
301 return null;
269 } 302 }
270} 303}
diff --git a/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java b/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java
index d7160e34..4b587d14 100644
--- a/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java
+++ b/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java
@@ -46,13 +46,16 @@ public class CarEnrolmentActivity extends Activity {
46 private static final String SP_HANDLE_KEY = "sp-test"; 46 private static final String SP_HANDLE_KEY = "sp-test";
47 private static final int FINE_LOCATION_REQUEST_CODE = 42; 47 private static final int FINE_LOCATION_REQUEST_CODE = 42;
48 48
49 /**
50 * Receives escrow token callbacks, registered on {@link CarTrustAgentBleService}
51 */
49 private final ICarTrustAgentTokenResponseCallback mCarTrustAgentTokenResponseCallback = 52 private final ICarTrustAgentTokenResponseCallback mCarTrustAgentTokenResponseCallback =
50 new ICarTrustAgentTokenResponseCallback.Stub() { 53 new ICarTrustAgentTokenResponseCallback.Stub() {
51 @Override 54 @Override
52 public void onEscrowTokenAdded(byte[] token, long handle, int uid) { 55 public void onEscrowTokenAdded(byte[] token, long handle, int uid) {
53 runOnUiThread(() -> { 56 runOnUiThread(() -> {
54 mPrefs.edit().putLong(SP_HANDLE_KEY, handle).apply(); 57 mPrefs.edit().putLong(SP_HANDLE_KEY, handle).apply();
55 Log.d(TAG, "stored new handle"); 58 Log.d(TAG, "stored new handle for user: " + uid);
56 }); 59 });
57 60
58 if (mBluetoothDevice == null) { 61 if (mBluetoothDevice == null) {
@@ -81,6 +84,9 @@ public class CarEnrolmentActivity extends Activity {
81 } 84 }
82 }; 85 };
83 86
87 /**
88 * Receives BLE state change callbacks, registered on {@link CarTrustAgentBleService}
89 */
84 private final ICarTrustAgentBleCallback mBleConnectionCallback = 90 private final ICarTrustAgentBleCallback mBleConnectionCallback =
85 new ICarTrustAgentBleCallback.Stub() { 91 new ICarTrustAgentBleCallback.Stub() {
86 @Override 92 @Override
@@ -108,6 +114,12 @@ public class CarEnrolmentActivity extends Activity {
108 } 114 }
109 }; 115 };
110 116
117 /**
118 * {@link CarTrustAgentBleService} will callback this when receives enrolment data.
119 *
120 * Here is the place we can prompt to the user on HU whether or not to add this
121 * {@link #mBluetoothDevice} as a trust device.
122 */
111 private final ICarTrustAgentEnrolmentCallback mEnrolmentCallback = 123 private final ICarTrustAgentEnrolmentCallback mEnrolmentCallback =
112 new ICarTrustAgentEnrolmentCallback.Stub() { 124 new ICarTrustAgentEnrolmentCallback.Stub() {
113 @Override 125 @Override
@@ -121,6 +133,9 @@ public class CarEnrolmentActivity extends Activity {
121 } 133 }
122 }; 134 };
123 135
136 /**
137 * Service connection to {@link CarTrustAgentBleService}
138 */
124 private final ServiceConnection mServiceConnection = new ServiceConnection() { 139 private final ServiceConnection mServiceConnection = new ServiceConnection() {
125 @Override 140 @Override
126 public void onServiceConnected(ComponentName name, IBinder service) { 141 public void onServiceConnected(ComponentName name, IBinder service) {
@@ -135,6 +150,7 @@ public class CarEnrolmentActivity extends Activity {
135 } catch (RemoteException e) { 150 } catch (RemoteException e) {
136 Log.e(TAG, "Error startEnrolmentAdvertising", e); 151 Log.e(TAG, "Error startEnrolmentAdvertising", e);
137 } 152 }
153 checkTokenHandle();
138 } 154 }
139 155
140 @Override 156 @Override
@@ -169,8 +185,10 @@ public class CarEnrolmentActivity extends Activity {
169 mPrefs = PreferenceManager.getDefaultSharedPreferences(this /* context */); 185 mPrefs = PreferenceManager.getDefaultSharedPreferences(this /* context */);
170 186
171 findViewById(R.id.start_button).setOnClickListener((view) -> { 187 findViewById(R.id.start_button).setOnClickListener((view) -> {
172 Intent bindIntent = new Intent(this, CarTrustAgentBleService.class); 188 if (!mCarTrustAgentBleServiceBound) {
173 bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE); 189 Intent bindIntent = new Intent(this, CarTrustAgentBleService.class);
190 bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
191 }
174 }); 192 });
175 193
176 findViewById(R.id.revoke_trust_button).setOnClickListener((view) -> { 194 findViewById(R.id.revoke_trust_button).setOnClickListener((view) -> {
@@ -193,33 +211,17 @@ public class CarEnrolmentActivity extends Activity {
193 requestPermissions( 211 requestPermissions(
194 new String[] { android.Manifest.permission.ACCESS_FINE_LOCATION }, 212 new String[] { android.Manifest.permission.ACCESS_FINE_LOCATION },
195 FINE_LOCATION_REQUEST_CODE); 213 FINE_LOCATION_REQUEST_CODE);
196 } else {
197 long tokenHandle = getTokenHandle();
198 if (tokenHandle != -1) {
199 Log.d(TAG, "onResume, checking handle active: " + tokenHandle);
200 if (mCarTrustAgentBleServiceBound) {
201 try {
202 // Due to the asynchronous nature of isEscrowTokenActive in
203 // TrustAgentService, query result will be delivered via
204 // {@link #mCarTrustAgentTokenResponseCallback}
205 mCarTrustAgentBleService.isEscrowTokenActive(tokenHandle,
206 UserHandle.myUserId());
207 } catch (RemoteException e) {
208 Log.e(TAG, "Error isEscrowTokenActive", e);
209 }
210 }
211 } else {
212 appendOutputText("No handles found");
213 }
214 } 214 }
215 } 215 }
216 216
217 @Override 217 @Override
218 protected void onDestroy() { 218 protected void onStop() {
219 super.onStop();
220
219 if (mCarTrustAgentBleServiceBound) { 221 if (mCarTrustAgentBleServiceBound) {
220 unbindService(mServiceConnection); 222 unbindService(mServiceConnection);
223 mCarTrustAgentBleServiceBound = false;
221 } 224 }
222 super.onDestroy();
223 } 225 }
224 226
225 private void appendOutputText(final String text) { 227 private void appendOutputText(final String text) {
@@ -234,7 +236,23 @@ public class CarEnrolmentActivity extends Activity {
234 mCarTrustAgentBleService.addEscrowToken(token, UserHandle.myUserId()); 236 mCarTrustAgentBleService.addEscrowToken(token, UserHandle.myUserId());
235 } 237 }
236 238
237 private long getTokenHandle() { 239 private void checkTokenHandle() {
238 return mPrefs.getLong(SP_HANDLE_KEY, -1); 240 long tokenHandle = mPrefs.getLong(SP_HANDLE_KEY, -1);
241 if (tokenHandle != -1) {
242 Log.d(TAG, "Checking handle active: " + tokenHandle);
243 if (mCarTrustAgentBleServiceBound) {
244 try {
245 // Due to the asynchronous nature of isEscrowTokenActive in
246 // TrustAgentService, query result will be delivered via
247 // {@link #mCarTrustAgentTokenResponseCallback}
248 mCarTrustAgentBleService.isEscrowTokenActive(tokenHandle,
249 UserHandle.myUserId());
250 } catch (RemoteException e) {
251 Log.e(TAG, "Error isEscrowTokenActive", e);
252 }
253 }
254 } else {
255 appendOutputText("No handles found");
256 }
239 } 257 }
240} 258}
diff --git a/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java b/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java
index f8ba90f3..2cc20800 100644
--- a/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java
+++ b/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java
@@ -25,6 +25,7 @@ import android.car.trust.ICarTrustAgentTokenRequestDelegate;
25import android.car.trust.ICarTrustAgentTokenResponseCallback; 25import android.car.trust.ICarTrustAgentTokenResponseCallback;
26import android.car.trust.ICarTrustAgentUnlockCallback; 26import android.car.trust.ICarTrustAgentUnlockCallback;
27import android.content.Intent; 27import android.content.Intent;
28import android.content.SharedPreferences;
28import android.os.IBinder; 29import android.os.IBinder;
29import android.os.ParcelUuid; 30import android.os.ParcelUuid;
30import android.os.RemoteCallbackList; 31import android.os.RemoteCallbackList;
@@ -62,6 +63,8 @@ public class CarTrustAgentBleService extends SimpleBleServer {
62 private byte[] mCurrentUnlockToken; 63 private byte[] mCurrentUnlockToken;
63 private Long mCurrentUnlockHandle; 64 private Long mCurrentUnlockHandle;
64 65
66 private SharedPreferences mTokenHandleSharedPreferences;
67
65 @Override 68 @Override
66 public void onCreate() { 69 public void onCreate() {
67 super.onCreate(); 70 super.onCreate();
@@ -72,6 +75,10 @@ public class CarTrustAgentBleService extends SimpleBleServer {
72 75
73 setupEnrolmentBleServer(); 76 setupEnrolmentBleServer();
74 setupUnlockBleServer(); 77 setupUnlockBleServer();
78
79 mTokenHandleSharedPreferences = getSharedPreferences(
80 getString(R.string.token_handle_shared_preferences),
81 MODE_PRIVATE);
75 } 82 }
76 83
77 @Override 84 @Override
@@ -260,8 +267,8 @@ public class CarTrustAgentBleService extends SimpleBleServer {
260 267
261 @Override 268 @Override
262 public void startEnrolmentAdvertising() { 269 public void startEnrolmentAdvertising() {
263 Log.d(TAG, "startEnrolmentAdvertising");
264 stopUnlockAdvertising(); 270 stopUnlockAdvertising();
271 Log.d(TAG, "startEnrolmentAdvertising");
265 startAdvertising(mEnrolmentUuid, mEnrolmentGattServer); 272 startAdvertising(mEnrolmentUuid, mEnrolmentGattServer);
266 } 273 }
267 274
@@ -290,8 +297,8 @@ public class CarTrustAgentBleService extends SimpleBleServer {
290 297
291 @Override 298 @Override
292 public void startUnlockAdvertising() { 299 public void startUnlockAdvertising() {
293 Log.d(TAG, "startUnlockAdvertising");
294 stopEnrolmentAdvertising(); 300 stopEnrolmentAdvertising();
301 Log.d(TAG, "startUnlockAdvertising");
295 startAdvertising(mUnlockUuid, mUnlockGattServer); 302 startAdvertising(mUnlockUuid, mUnlockGattServer);
296 } 303 }
297 304
@@ -353,6 +360,9 @@ public class CarTrustAgentBleService extends SimpleBleServer {
353 public void onEscrowTokenAdded(byte[] token, long handle, int uid) 360 public void onEscrowTokenAdded(byte[] token, long handle, int uid)
354 throws RemoteException { 361 throws RemoteException {
355 Log.d(TAG, "onEscrowTokenAdded handle:" + handle + " uid:" + uid); 362 Log.d(TAG, "onEscrowTokenAdded handle:" + handle + " uid:" + uid);
363 mTokenHandleSharedPreferences.edit()
364 .putInt(String.valueOf(handle), uid)
365 .apply();
356 if (mTokenResponseCallback != null) { 366 if (mTokenResponseCallback != null) {
357 mTokenResponseCallback.onEscrowTokenAdded(token, handle, uid); 367 mTokenResponseCallback.onEscrowTokenAdded(token, handle, uid);
358 } 368 }
@@ -361,6 +371,9 @@ public class CarTrustAgentBleService extends SimpleBleServer {
361 @Override 371 @Override
362 public void onEscrowTokenRemoved(long handle, boolean successful) throws RemoteException { 372 public void onEscrowTokenRemoved(long handle, boolean successful) throws RemoteException {
363 Log.d(TAG, "onEscrowTokenRemoved handle:" + handle); 373 Log.d(TAG, "onEscrowTokenRemoved handle:" + handle);
374 mTokenHandleSharedPreferences.edit()
375 .remove(String.valueOf(handle))
376 .apply();
364 if (mTokenResponseCallback != null) { 377 if (mTokenResponseCallback != null) {
365 mTokenResponseCallback.onEscrowTokenRemoved(handle, successful); 378 mTokenResponseCallback.onEscrowTokenRemoved(handle, successful);
366 } 379 }
@@ -373,5 +386,10 @@ public class CarTrustAgentBleService extends SimpleBleServer {
373 mTokenResponseCallback.onEscrowTokenActiveStateChanged(handle, active); 386 mTokenResponseCallback.onEscrowTokenActiveStateChanged(handle, active);
374 } 387 }
375 } 388 }
389
390 @Override
391 public int getUserIdByEscrowTokenHandle(long tokenHandle) {
392 return mTokenHandleSharedPreferences.getInt(String.valueOf(tokenHandle), -1);
393 }
376 } 394 }
377} 395}
diff --git a/car-lib/api/current.txt b/car-lib/api/current.txt
index 9cd2e4c3..57975a2c 100644
--- a/car-lib/api/current.txt
+++ b/car-lib/api/current.txt
@@ -73,6 +73,126 @@ package android.car {
73 ctor public CarNotConnectedException(java.lang.Exception); 73 ctor public CarNotConnectedException(java.lang.Exception);
74 } 74 }
75 75
76 public final class VehiclePropertyIds {
77 ctor public VehiclePropertyIds();
78 method public static java.lang.String toString(int);
79 field public static final int ABS_ACTIVE = 287310858; // 0x1120040a
80 field public static final int AP_POWER_BOOTUP_REASON = 289409538; // 0x11400a02
81 field public static final int AP_POWER_STATE_REPORT = 289475073; // 0x11410a01
82 field public static final int AP_POWER_STATE_REQ = 289475072; // 0x11410a00
83 field public static final int CURRENT_GEAR = 289408001; // 0x11400401
84 field public static final int DISPLAY_BRIGHTNESS = 289409539; // 0x11400a03
85 field public static final int DOOR_LOCK = 371198722; // 0x16200b02
86 field public static final int DOOR_MOVE = 373295873; // 0x16400b01
87 field public static final int DOOR_POS = 373295872; // 0x16400b00
88 field public static final int ENGINE_COOLANT_TEMP = 291504897; // 0x11600301
89 field public static final int ENGINE_OIL_LEVEL = 289407747; // 0x11400303
90 field public static final int ENGINE_OIL_TEMP = 291504900; // 0x11600304
91 field public static final int ENGINE_RPM = 291504901; // 0x11600305
92 field public static final int ENV_OUTSIDE_TEMPERATURE = 291505923; // 0x11600703
93 field public static final int EV_BATTERY_INSTANTANEOUS_CHARGE_RATE = 291504908; // 0x1160030c
94 field public static final int EV_BATTERY_LEVEL = 291504905; // 0x11600309
95 field public static final int EV_CHARGE_PORT_CONNECTED = 287310603; // 0x1120030b
96 field public static final int EV_CHARGE_PORT_OPEN = 287310602; // 0x1120030a
97 field public static final int FOG_LIGHTS_STATE = 289410562; // 0x11400e02
98 field public static final int FOG_LIGHTS_SWITCH = 289410578; // 0x11400e12
99 field public static final int FUEL_DOOR_OPEN = 287310600; // 0x11200308
100 field public static final int FUEL_LEVEL = 291504903; // 0x11600307
101 field public static final int FUEL_LEVEL_LOW = 287310853; // 0x11200405
102 field public static final int GEAR_SELECTION = 289408000; // 0x11400400
103 field public static final int HAZARD_LIGHTS_STATE = 289410563; // 0x11400e03
104 field public static final int HAZARD_LIGHTS_SWITCH = 289410579; // 0x11400e13
105 field public static final int HEADLIGHTS_STATE = 289410560; // 0x11400e00
106 field public static final int HEADLIGHTS_SWITCH = 289410576; // 0x11400e10
107 field public static final int HIGH_BEAM_LIGHTS_STATE = 289410561; // 0x11400e01
108 field public static final int HIGH_BEAM_LIGHTS_SWITCH = 289410577; // 0x11400e11
109 field public static final int HVAC_ACTUAL_FAN_SPEED_RPM = 356517135; // 0x1540050f
110 field public static final int HVAC_AC_ON = 354419973; // 0x15200505
111 field public static final int HVAC_AUTO_ON = 354419978; // 0x1520050a
112 field public static final int HVAC_AUTO_RECIRC_ON = 354419986; // 0x15200512
113 field public static final int HVAC_DEFROSTER = 320865540; // 0x13200504
114 field public static final int HVAC_DUAL_ON = 354419977; // 0x15200509
115 field public static final int HVAC_FAN_DIRECTION = 356517121; // 0x15400501
116 field public static final int HVAC_FAN_DIRECTION_AVAILABLE = 356582673; // 0x15410511
117 field public static final int HVAC_FAN_SPEED = 356517120; // 0x15400500
118 field public static final int HVAC_MAX_AC_ON = 354419974; // 0x15200506
119 field public static final int HVAC_MAX_DEFROST_ON = 354419975; // 0x15200507
120 field public static final int HVAC_POWER_ON = 354419984; // 0x15200510
121 field public static final int HVAC_RECIRC_ON = 354419976; // 0x15200508
122 field public static final int HVAC_SEAT_TEMPERATURE = 356517131; // 0x1540050b
123 field public static final int HVAC_SEAT_VENTILATION = 356517139; // 0x15400513
124 field public static final int HVAC_SIDE_MIRROR_HEAT = 339739916; // 0x1440050c
125 field public static final int HVAC_STEERING_WHEEL_HEAT = 289408269; // 0x1140050d
126 field public static final int HVAC_TEMPERATURE_CURRENT = 358614274; // 0x15600502
127 field public static final int HVAC_TEMPERATURE_DISPLAY_UNITS = 289408270; // 0x1140050e
128 field public static final int HVAC_TEMPERATURE_SET = 358614275; // 0x15600503
129 field public static final int HW_KEY_INPUT = 289475088; // 0x11410a10
130 field public static final int IGNITION_STATE = 289408009; // 0x11400409
131 field public static final int INFO_DRIVER_SEAT = 356516106; // 0x1540010a
132 field public static final int INFO_EV_BATTERY_CAPACITY = 291504390; // 0x11600106
133 field public static final int INFO_EV_CONNECTOR_TYPE = 289472775; // 0x11410107
134 field public static final int INFO_EV_PORT_LOCATION = 289407241; // 0x11400109
135 field public static final int INFO_FUEL_CAPACITY = 291504388; // 0x11600104
136 field public static final int INFO_FUEL_DOOR_LOCATION = 289407240; // 0x11400108
137 field public static final int INFO_FUEL_TYPE = 289472773; // 0x11410105
138 field public static final int INFO_MAKE = 286261505; // 0x11100101
139 field public static final int INFO_MODEL = 286261506; // 0x11100102
140 field public static final int INFO_MODEL_YEAR = 289407235; // 0x11400103
141 field public static final int INFO_VIN = 286261504; // 0x11100100
142 field public static final int INVALID = 0; // 0x0
143 field public static final int MIRROR_FOLD = 287312709; // 0x11200b45
144 field public static final int MIRROR_LOCK = 287312708; // 0x11200b44
145 field public static final int MIRROR_Y_MOVE = 339741507; // 0x14400b43
146 field public static final int MIRROR_Y_POS = 339741506; // 0x14400b42
147 field public static final int MIRROR_Z_MOVE = 339741505; // 0x14400b41
148 field public static final int MIRROR_Z_POS = 339741504; // 0x14400b40
149 field public static final int NIGHT_MODE = 287310855; // 0x11200407
150 field public static final int OBD2_FREEZE_FRAME = 299896065; // 0x11e00d01
151 field public static final int OBD2_FREEZE_FRAME_CLEAR = 299896067; // 0x11e00d03
152 field public static final int OBD2_FREEZE_FRAME_INFO = 299896066; // 0x11e00d02
153 field public static final int OBD2_LIVE_FRAME = 299896064; // 0x11e00d00
154 field public static final int PARKING_BRAKE_AUTO_APPLY = 287310851; // 0x11200403
155 field public static final int PARKING_BRAKE_ON = 287310850; // 0x11200402
156 field public static final int PERF_ODOMETER = 291504644; // 0x11600204
157 field public static final int PERF_VEHICLE_SPEED = 291504647; // 0x11600207
158 field public static final int RANGE_REMAINING = 291504904; // 0x11600308
159 field public static final int SEAT_BACKREST_ANGLE_1_MOVE = 356518792; // 0x15400b88
160 field public static final int SEAT_BACKREST_ANGLE_1_POS = 356518791; // 0x15400b87
161 field public static final int SEAT_BACKREST_ANGLE_2_MOVE = 356518794; // 0x15400b8a
162 field public static final int SEAT_BACKREST_ANGLE_2_POS = 356518793; // 0x15400b89
163 field public static final int SEAT_BELT_BUCKLED = 354421634; // 0x15200b82
164 field public static final int SEAT_BELT_HEIGHT_MOVE = 356518788; // 0x15400b84
165 field public static final int SEAT_BELT_HEIGHT_POS = 356518787; // 0x15400b83
166 field public static final int SEAT_DEPTH_MOVE = 356518798; // 0x15400b8e
167 field public static final int SEAT_DEPTH_POS = 356518797; // 0x15400b8d
168 field public static final int SEAT_FORE_AFT_MOVE = 356518790; // 0x15400b86
169 field public static final int SEAT_FORE_AFT_POS = 356518789; // 0x15400b85
170 field public static final int SEAT_HEADREST_ANGLE_MOVE = 356518808; // 0x15400b98
171 field public static final int SEAT_HEADREST_ANGLE_POS = 356518807; // 0x15400b97
172 field public static final int SEAT_HEADREST_FORE_AFT_MOVE = 356518810; // 0x15400b9a
173 field public static final int SEAT_HEADREST_FORE_AFT_POS = 356518809; // 0x15400b99
174 field public static final int SEAT_HEADREST_HEIGHT_MOVE = 356518806; // 0x15400b96
175 field public static final int SEAT_HEADREST_HEIGHT_POS = 289409941; // 0x11400b95
176 field public static final int SEAT_HEIGHT_MOVE = 356518796; // 0x15400b8c
177 field public static final int SEAT_HEIGHT_POS = 356518795; // 0x15400b8b
178 field public static final int SEAT_LUMBAR_FORE_AFT_MOVE = 356518802; // 0x15400b92
179 field public static final int SEAT_LUMBAR_FORE_AFT_POS = 356518801; // 0x15400b91
180 field public static final int SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 356518804; // 0x15400b94
181 field public static final int SEAT_LUMBAR_SIDE_SUPPORT_POS = 356518803; // 0x15400b93
182 field public static final int SEAT_MEMORY_SELECT = 356518784; // 0x15400b80
183 field public static final int SEAT_MEMORY_SET = 356518785; // 0x15400b81
184 field public static final int SEAT_TILT_MOVE = 356518800; // 0x15400b90
185 field public static final int SEAT_TILT_POS = 356518799; // 0x15400b8f
186 field public static final int TIRE_PRESSURE = 392168201; // 0x17600309
187 field public static final int TRACTION_CONTROL_ACTIVE = 287310859; // 0x1120040b
188 field public static final int TURN_SIGNAL_STATE = 289408008; // 0x11400408
189 field public static final int VEHICLE_MAP_SERVICE = 299895808; // 0x11e00c00
190 field public static final int WHEEL_TICK = 290521862; // 0x11510306
191 field public static final int WINDOW_LOCK = 320867268; // 0x13200bc4
192 field public static final int WINDOW_MOVE = 322964417; // 0x13400bc1
193 field public static final int WINDOW_POS = 322964416; // 0x13400bc0
194 }
195
76} 196}
77 197
78package android.car.app.menu { 198package android.car.app.menu {
@@ -265,7 +385,6 @@ package android.car.hardware {
265 field public static final int IGNITION_STATE_ON = 4; // 0x4 385 field public static final int IGNITION_STATE_ON = 4; // 0x4
266 field public static final int IGNITION_STATE_START = 5; // 0x5 386 field public static final int IGNITION_STATE_START = 5; // 0x5
267 field public static final int IGNITION_STATE_UNDEFINED = 0; // 0x0 387 field public static final int IGNITION_STATE_UNDEFINED = 0; // 0x0
268 field public static final int INDEX_ENVIRONMENT_PRESSURE = 1; // 0x1
269 field public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0; // 0x0 388 field public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0; // 0x0
270 field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1 389 field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1
271 field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2 390 field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2
@@ -280,7 +399,6 @@ package android.car.hardware {
280 } 399 }
281 400
282 public static class CarSensorEvent.EnvironmentData { 401 public static class CarSensorEvent.EnvironmentData {
283 field public float pressure;
284 field public float temperature; 402 field public float temperature;
285 field public long timestamp; 403 field public long timestamp;
286 } 404 }
@@ -300,7 +418,7 @@ package android.car.hardware {
300 field public static final int SENSOR_RATE_UI = 5; // 0x5 418 field public static final int SENSOR_RATE_UI = 5; // 0x5
301 field public static final int SENSOR_TYPE_ABS_ACTIVE = 287310858; // 0x1120040a 419 field public static final int SENSOR_TYPE_ABS_ACTIVE = 287310858; // 0x1120040a
302 field public static final int SENSOR_TYPE_CAR_SPEED = 291504647; // 0x11600207 420 field public static final int SENSOR_TYPE_CAR_SPEED = 291504647; // 0x11600207
303 field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc 421 field public static final int SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE = 291505923; // 0x11600703
304 field public static final int SENSOR_TYPE_EV_BATTERY_CHARGE_RATE = 291504908; // 0x1160030c 422 field public static final int SENSOR_TYPE_EV_BATTERY_CHARGE_RATE = 291504908; // 0x1160030c
305 field public static final int SENSOR_TYPE_EV_BATTERY_LEVEL = 291504905; // 0x11600309 423 field public static final int SENSOR_TYPE_EV_BATTERY_LEVEL = 291504905; // 0x11600309
306 field public static final int SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED = 287310603; // 0x1120030b 424 field public static final int SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED = 287310603; // 0x1120030b
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index d6119326..df6b85ed 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -5,7 +5,6 @@ package android.car {
5 field public static final java.lang.String CAR_DRIVING_STATE_SERVICE = "drivingstate"; 5 field public static final java.lang.String CAR_DRIVING_STATE_SERVICE = "drivingstate";
6 field public static final java.lang.String DIAGNOSTIC_SERVICE = "diagnostic"; 6 field public static final java.lang.String DIAGNOSTIC_SERVICE = "diagnostic";
7 field public static final java.lang.String HVAC_SERVICE = "hvac"; 7 field public static final java.lang.String HVAC_SERVICE = "hvac";
8 field public static final java.lang.String PERMISSION_ADJUST_CAR_CABIN = "android.car.permission.ADJUST_CAR_CABIN";
9 field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.CLEAR_CAR_DIAGNOSTICS"; 8 field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.CLEAR_CAR_DIAGNOSTICS";
10 field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_READ_ALL = "android.car.permission.CAR_DIAGNOSTICS"; 9 field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_READ_ALL = "android.car.permission.CAR_DIAGNOSTICS";
11 field public static final java.lang.String PERMISSION_CAR_DRIVING_STATE = "android.car.permission.CAR_DRIVING_STATE"; 10 field public static final java.lang.String PERMISSION_CAR_DRIVING_STATE = "android.car.permission.CAR_DRIVING_STATE";
@@ -112,6 +111,13 @@ package android.car {
112 field public static final int WINDOW_ROW_3_RIGHT = 16384; // 0x4000 111 field public static final int WINDOW_ROW_3_RIGHT = 16384; // 0x4000
113 } 112 }
114 113
114 public final class VehiclePropertyAccess {
115 field public static final int NONE = 0; // 0x0
116 field public static final int READ = 1; // 0x1
117 field public static final int READ_WRITE = 3; // 0x3
118 field public static final int WRITE = 2; // 0x2
119 }
120
115} 121}
116 122
117package android.car.cluster { 123package android.car.cluster {
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 738a4238..c1bf5027 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -281,14 +281,6 @@ public final class Car {
281 "android.car.permission.CONTROL_APP_BLOCKING"; 281 "android.car.permission.CONTROL_APP_BLOCKING";
282 282
283 /** 283 /**
284 * Permission necessary to access Car Cabin APIs.
285 * @hide
286 */
287 @SystemApi
288 public static final String PERMISSION_ADJUST_CAR_CABIN =
289 "android.car.permission.ADJUST_CAR_CABIN";
290
291 /**
292 * Permission necessary to access car's engine information. 284 * Permission necessary to access car's engine information.
293 * @hide 285 * @hide
294 */ 286 */
diff --git a/car-lib/src/android/car/CarInfoManager.java b/car-lib/src/android/car/CarInfoManager.java
index b228ca7c..d90883b6 100644
--- a/car-lib/src/android/car/CarInfoManager.java
+++ b/car-lib/src/android/car/CarInfoManager.java
@@ -16,16 +16,12 @@
16 16
17package android.car; 17package android.car;
18 18
19import static java.lang.Integer.toHexString;
20
21import android.annotation.Nullable; 19import android.annotation.Nullable;
22import android.car.annotation.ValueTypeDef; 20import android.car.annotation.ValueTypeDef;
23import android.car.hardware.CarPropertyValue; 21import android.car.hardware.CarPropertyValue;
24import android.car.hardware.property.ICarProperty; 22import android.car.hardware.property.CarPropertyManager;
25import android.os.Bundle; 23import android.os.Bundle;
26import android.os.IBinder; 24import android.os.IBinder;
27import android.os.RemoteException;
28import android.util.Log;
29 25
30 26
31/** 27/**
@@ -36,6 +32,7 @@ public final class CarInfoManager implements CarManagerBase{
36 32
37 private static final boolean DBG = false; 33 private static final boolean DBG = false;
38 private static final String TAG = "CarInfoManager"; 34 private static final String TAG = "CarInfoManager";
35 private final CarPropertyManager mCarPropertyMgr;
39 /** 36 /**
40 * Key for manufacturer of the car. Passed in basic info Bundle. 37 * Key for manufacturer of the car. Passed in basic info Bundle.
41 * @hide 38 * @hide
@@ -112,17 +109,15 @@ public final class CarInfoManager implements CarManagerBase{
112 * Passed in basic info Bundle. 109 * Passed in basic info Bundle.
113 * @hide 110 * @hide
114 */ 111 */
115 @ValueTypeDef(type = Integer.class) 112 @ValueTypeDef(type = Integer[].class)
116 public static final int BASIC_INFO_EV_CONNECTOR_TYPES = 0x11410107; 113 public static final int BASIC_INFO_EV_CONNECTOR_TYPES = 0x11410107;
117 114
118 private final ICarProperty mService;
119
120 /** 115 /**
121 * @return Manufacturer of the car. Null if not available. 116 * @return Manufacturer of the car. Null if not available.
122 */ 117 */
123 @Nullable 118 @Nullable
124 public String getManufacturer() throws CarNotConnectedException { 119 public String getManufacturer() throws CarNotConnectedException {
125 CarPropertyValue<String> carProp = getProperty(String.class, 120 CarPropertyValue<String> carProp = mCarPropertyMgr.getProperty(String.class,
126 BASIC_INFO_KEY_MANUFACTURER, 0); 121 BASIC_INFO_KEY_MANUFACTURER, 0);
127 return carProp != null ? carProp.getValue() : null; 122 return carProp != null ? carProp.getValue() : null;
128 } 123 }
@@ -134,7 +129,8 @@ public final class CarInfoManager implements CarManagerBase{
134 */ 129 */
135 @Nullable 130 @Nullable
136 public String getModel() throws CarNotConnectedException { 131 public String getModel() throws CarNotConnectedException {
137 CarPropertyValue<String> carProp = getProperty(String.class, BASIC_INFO_KEY_MODEL, 0); 132 CarPropertyValue<String> carProp = mCarPropertyMgr.getProperty(
133 String.class, BASIC_INFO_KEY_MODEL, 0);
138 return carProp != null ? carProp.getValue() : null; 134 return carProp != null ? carProp.getValue() : null;
139 } 135 }
140 136
@@ -143,7 +139,7 @@ public final class CarInfoManager implements CarManagerBase{
143 */ 139 */
144 @Nullable 140 @Nullable
145 public String getModelYear() throws CarNotConnectedException { 141 public String getModelYear() throws CarNotConnectedException {
146 CarPropertyValue<String> carProp = getProperty(String.class, 142 CarPropertyValue<String> carProp = mCarPropertyMgr.getProperty(String.class,
147 BASIC_INFO_KEY_MODEL_YEAR, 0); 143 BASIC_INFO_KEY_MODEL_YEAR, 0);
148 return carProp != null ? carProp.getValue() : null; 144 return carProp != null ? carProp.getValue() : null;
149 } 145 }
@@ -163,7 +159,7 @@ public final class CarInfoManager implements CarManagerBase{
163 * fuel. 159 * fuel.
164 */ 160 */
165 public float getFuelCapacity() throws CarNotConnectedException { 161 public float getFuelCapacity() throws CarNotConnectedException {
166 CarPropertyValue<Float> carProp = getProperty(Float.class, 162 CarPropertyValue<Float> carProp = mCarPropertyMgr.getProperty(Float.class,
167 BASIC_INFO_FUEL_CAPACITY, 0); 163 BASIC_INFO_FUEL_CAPACITY, 0);
168 return carProp != null ? carProp.getValue() : 0f; 164 return carProp != null ? carProp.getValue() : 0f;
169 } 165 }
@@ -173,8 +169,7 @@ public final class CarInfoManager implements CarManagerBase{
173 * types available. 169 * types available.
174 */ 170 */
175 public @FuelType.Enum int[] getFuelTypes() throws CarNotConnectedException { 171 public @FuelType.Enum int[] getFuelTypes() throws CarNotConnectedException {
176 CarPropertyValue<int[]> carProp = getProperty(int[].class, BASIC_INFO_FUEL_TYPES, 0); 172 return mCarPropertyMgr.getIntArrayProperty(BASIC_INFO_FUEL_TYPES, 0);
177 return carProp != null ? carProp.getValue() : new int[0];
178 } 173 }
179 174
180 /** 175 /**
@@ -182,7 +177,7 @@ public final class CarInfoManager implements CarManagerBase{
182 * battery. 177 * battery.
183 */ 178 */
184 public float getEvBatteryCapacity() throws CarNotConnectedException { 179 public float getEvBatteryCapacity() throws CarNotConnectedException {
185 CarPropertyValue<Float> carProp = getProperty(Float.class, 180 CarPropertyValue<Float> carProp = mCarPropertyMgr.getProperty(Float.class,
186 BASIC_INFO_EV_BATTERY_CAPACITY, 0); 181 BASIC_INFO_EV_BATTERY_CAPACITY, 0);
187 return carProp != null ? carProp.getValue() : 0f; 182 return carProp != null ? carProp.getValue() : 0f;
188 } 183 }
@@ -192,42 +187,17 @@ public final class CarInfoManager implements CarManagerBase{
192 * no connector types available. 187 * no connector types available.
193 */ 188 */
194 public @EvConnectorType.Enum int[] getEvConnectorTypes() throws CarNotConnectedException { 189 public @EvConnectorType.Enum int[] getEvConnectorTypes() throws CarNotConnectedException {
195 CarPropertyValue<int[]> carProp = getProperty(int[].class, 190 return mCarPropertyMgr.getIntArrayProperty(BASIC_INFO_EV_CONNECTOR_TYPES, 0);
196 BASIC_INFO_EV_CONNECTOR_TYPES, 0);
197 return carProp != null ? carProp.getValue() : new int[0];
198 } 191 }
199 192
200 /** @hide */ 193 /** @hide */
201 CarInfoManager(IBinder service) { 194 CarInfoManager(IBinder service) {
202 mService = ICarProperty.Stub.asInterface(service); 195 mCarPropertyMgr = new CarPropertyManager(service, null, DBG, TAG);
203 } 196 }
204 197
205 /** @hide */ 198 /** @hide */
206 public void onCarDisconnected() { 199 public void onCarDisconnected() {
200 mCarPropertyMgr.onCarDisconnected();
207 } 201 }
208 202
209 private <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area)
210 throws CarNotConnectedException {
211 if (DBG) {
212 Log.d(TAG, "getProperty, propId: 0x" + toHexString(propId)
213 + ", area: 0x" + toHexString(area) + ", class: " + clazz);
214 }
215 try {
216 CarPropertyValue<E> propVal = mService.getProperty(propId, area);
217 if (propVal != null && propVal.getValue() != null) {
218 Class<?> actualClass = propVal.getValue().getClass();
219 if (actualClass != clazz) {
220 throw new IllegalArgumentException("Invalid property type. " + "Expected: "
221 + clazz + ", but was: " + actualClass);
222 }
223 }
224 return propVal;
225 } catch (RemoteException e) {
226 Log.e(TAG, "getProperty failed with " + e.toString()
227 + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
228 throw new CarNotConnectedException(e);
229 } catch (IllegalArgumentException e) {
230 return null;
231 }
232 }
233} 203}
diff --git a/car-lib/src/android/car/PortLocationType.java b/car-lib/src/android/car/PortLocationType.java
new file mode 100644
index 00000000..3006a296
--- /dev/null
+++ b/car-lib/src/android/car/PortLocationType.java
@@ -0,0 +1,35 @@
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 */
16package android.car;
17
18/**
19 * Used by INFO_FUEL_DOOR_LOCATION/INFO_CHARGE_PORT_LOCATION to enumerate fuel door or
20 * ev port location.
21 * Use getProperty and setProperty in {@link android.car.hardware.property.CarPropertyManager} to
22 * set and get this VHAL property.
23 * @hide
24 */
25public final class PortLocationType {
26 public static final int UNKNOWN = 0;
27 public static final int FRONT_LEFT = 1;
28 public static final int FRONT_RIGHT = 2;
29 public static final int REAR_RIGHT = 3;
30 public static final int REAR_LEFT = 4;
31 public static final int FRONT = 5;
32 public static final int REAR = 6;
33
34 private PortLocationType() {}
35}
diff --git a/car-lib/src/android/car/VehicleLightState.java b/car-lib/src/android/car/VehicleLightState.java
new file mode 100644
index 00000000..55f74f9e
--- /dev/null
+++ b/car-lib/src/android/car/VehicleLightState.java
@@ -0,0 +1,31 @@
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 */
16package android.car;
17
18/**
19 * Used by Lights state properties to enumerate the current state of the lights.
20 * Use getProperty and setProperty in {@link android.car.hardware.property.CarPropertyManager} to
21 * set and get this VHAL property.
22 * @hide
23 */
24public final class VehicleLightState {
25 public static final int OFF = 0;
26 public static final int ON = 1;
27 public static final int DAYTIME_RUNNING = 2;
28
29 private VehicleLightState() {}
30
31}
diff --git a/car-lib/src/android/car/VehicleLightSwitch.java b/car-lib/src/android/car/VehicleLightSwitch.java
new file mode 100644
index 00000000..03780bb6
--- /dev/null
+++ b/car-lib/src/android/car/VehicleLightSwitch.java
@@ -0,0 +1,31 @@
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 */
16package android.car;
17
18/**
19 * Used by lights switch properties to enumerate user selected switch setting.
20 * Use getProperty and setProperty in {@link android.car.hardware.property.CarPropertyManager} to
21 * set and get this VHAL property.
22 * @hide
23 */
24public final class VehicleLightSwitch {
25 public static final int OFF = 0;
26 public static final int ON = 1;
27 public static final int DAYTIME_RUNNING = 2;
28 public static final int AUTOMATIC = 0x100;
29
30 private VehicleLightSwitch() {}
31}
diff --git a/car-lib/src/android/car/VehiclePropertyAccess.java b/car-lib/src/android/car/VehiclePropertyAccess.java
new file mode 100644
index 00000000..22801de4
--- /dev/null
+++ b/car-lib/src/android/car/VehiclePropertyAccess.java
@@ -0,0 +1,35 @@
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 */
16package android.car;
17
18import android.annotation.SystemApi;
19
20/**
21 * Define value for getAccess() in {@link android.car.hardware.CarPropertyConfig}
22 * @hide
23 */
24@SystemApi
25public final class VehiclePropertyAccess {
26 /**
27 * List of VehiclePropertyAccess from VHAL
28 */
29 public static final int NONE = 0x00;
30 public static final int READ = 0x01;
31 public static final int WRITE = 0x02;
32 public static final int READ_WRITE = 0x03;
33
34 private VehiclePropertyAccess() {}
35}
diff --git a/car-lib/src/android/car/VehiclePropertyIds.java b/car-lib/src/android/car/VehiclePropertyIds.java
new file mode 100644
index 00000000..12d2698d
--- /dev/null
+++ b/car-lib/src/android/car/VehiclePropertyIds.java
@@ -0,0 +1,888 @@
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
17package android.car;
18
19/**
20 * Copy from android.hardware.automotive.vehicle-V2.0-java_gen_java/gen/android/hardware/automotive
21 * /vehicle/V2_0. Need to update this file when vehicle propertyId is changed in VHAL.
22 * Use it as PorpertyId in getProperty() and setProperty() in
23 * {@link android.car.hardware.property.CarPropertyManager}
24 */
25public final class VehiclePropertyIds {
26 /**
27 * Undefined property. */
28 public static final int INVALID = 0;
29 /**
30 * VIN of vehicle
31 */
32 public static final int INFO_VIN = 286261504;
33 /**
34 * Manufacturer of vehicle
35 */
36 public static final int INFO_MAKE = 286261505;
37 /**
38 * Model of vehicle
39 */
40 public static final int INFO_MODEL = 286261506;
41 /**
42 * Model year of vehicle.
43 */
44 public static final int INFO_MODEL_YEAR = 289407235;
45 /**
46 * Fuel capacity of the vehicle in milliliters
47 */
48 public static final int INFO_FUEL_CAPACITY = 291504388;
49 /**
50 * List of fuels the vehicle may use
51 */
52 public static final int INFO_FUEL_TYPE = 289472773;
53 /**
54 * Battery capacity of the vehicle, if EV or hybrid. This is the nominal
55 * battery capacity when the vehicle is new.
56 */
57 public static final int INFO_EV_BATTERY_CAPACITY = 291504390;
58 /**
59 * List of connectors this EV may use
60 */
61 public static final int INFO_EV_CONNECTOR_TYPE = 289472775;
62 /**
63 * Fuel door location
64 */
65 public static final int INFO_FUEL_DOOR_LOCATION = 289407240;
66 /**
67 * EV port location
68 */
69 public static final int INFO_EV_PORT_LOCATION = 289407241;
70 /**
71 * Driver's seat location
72 */
73 public static final int INFO_DRIVER_SEAT = 356516106;
74 /**
75 * Current odometer value of the vehicle
76 */
77 public static final int PERF_ODOMETER = 291504644;
78 /**
79 * Speed of the vehicle
80 */
81 public static final int PERF_VEHICLE_SPEED = 291504647;
82 /**
83 * Temperature of engine coolant
84 */
85 public static final int ENGINE_COOLANT_TEMP = 291504897;
86 /**
87 * Engine oil level
88 */
89 public static final int ENGINE_OIL_LEVEL = 289407747;
90 /**
91 * Temperature of engine oil
92 */
93 public static final int ENGINE_OIL_TEMP = 291504900;
94 /**
95 * Engine rpm
96 */
97 public static final int ENGINE_RPM = 291504901;
98 /**
99 * Reports wheel ticks
100 */
101 public static final int WHEEL_TICK = 290521862;
102 /**
103 * Fuel remaining in the the vehicle, in milliliters
104 */
105 public static final int FUEL_LEVEL = 291504903;
106 /**
107 * Fuel door open
108 */
109 public static final int FUEL_DOOR_OPEN = 287310600;
110 /**
111 * EV battery level in WH, if EV or hybrid
112 */
113 public static final int EV_BATTERY_LEVEL = 291504905;
114 /**
115 * EV charge port open
116 */
117 public static final int EV_CHARGE_PORT_OPEN = 287310602;
118 /**
119 * EV charge port connected
120 */
121 public static final int EV_CHARGE_PORT_CONNECTED = 287310603;
122 /**
123 * EV instantaneous charge rate in milliwatts
124 */
125 public static final int EV_BATTERY_INSTANTANEOUS_CHARGE_RATE = 291504908;
126 /**
127 * Range remaining
128 */
129 public static final int RANGE_REMAINING = 291504904;
130 /**
131 * Tire pressure
132 *
133 * min/max value indicates tire pressure sensor range. Each tire will have a separate min/max
134 * value denoted by its areaConfig.areaId.
135 */
136 public static final int TIRE_PRESSURE = 392168201;
137 /**
138 * Currently selected gear
139 *
140 * This is the gear selected by the user.
141 */
142 public static final int GEAR_SELECTION = 289408000;
143 /**
144 * Current gear. In non-manual case, selected gear may not
145 * match the current gear. For example, if the selected gear is GEAR_DRIVE,
146 * the current gear will be one of GEAR_1, GEAR_2 etc, which reflects
147 * the actual gear the transmission is currently running in.
148 */
149 public static final int CURRENT_GEAR = 289408001;
150 /**
151 * Parking brake state.
152 */
153 public static final int PARKING_BRAKE_ON = 287310850;
154 /**
155 * Auto-apply parking brake.
156 */
157 public static final int PARKING_BRAKE_AUTO_APPLY = 287310851;
158 /**
159 * Warning for fuel low level.
160 */
161 public static final int FUEL_LEVEL_LOW = 287310853;
162 /**
163 * Night mode
164 */
165 public static final int NIGHT_MODE = 287310855;
166 /**
167 * State of the vehicles turn signals
168 */
169 public static final int TURN_SIGNAL_STATE = 289408008;
170 /**
171 * Represents ignition state
172 */
173 public static final int IGNITION_STATE = 289408009;
174 /**
175 * ABS is active
176 */
177 public static final int ABS_ACTIVE = 287310858;
178 /**
179 * Traction Control is active
180 */
181 public static final int TRACTION_CONTROL_ACTIVE = 287310859;
182 /**
183 * Fan speed setting
184 */
185 public static final int HVAC_FAN_SPEED = 356517120;
186 /**
187 * Fan direction setting
188 */
189 public static final int HVAC_FAN_DIRECTION = 356517121;
190 /**
191 * HVAC current temperature.
192 */
193 public static final int HVAC_TEMPERATURE_CURRENT = 358614274;
194 /**
195 * HVAC, target temperature set.
196 */
197 public static final int HVAC_TEMPERATURE_SET = 358614275;
198 /**
199 * On/off defrost for designated window
200 */
201 public static final int HVAC_DEFROSTER = 320865540;
202 /**
203 * On/off AC for designated areaId
204 */
205 public static final int HVAC_AC_ON = 354419973;
206 /**
207 * On/off max AC
208 */
209 public static final int HVAC_MAX_AC_ON = 354419974;
210 /**
211 * On/off max defrost
212 */
213 public static final int HVAC_MAX_DEFROST_ON = 354419975;
214 /**
215 * Recirculation on/off
216 */
217 public static final int HVAC_RECIRC_ON = 354419976;
218 /**
219 * Enable temperature coupling between areas.
220 */
221 public static final int HVAC_DUAL_ON = 354419977;
222 /**
223 * On/off automatic mode
224 */
225 public static final int HVAC_AUTO_ON = 354419978;
226 /**
227 * Seat heating/cooling
228 *
229 */
230 public static final int HVAC_SEAT_TEMPERATURE = 356517131;
231 /**
232 * Side Mirror Heat
233 */
234 public static final int HVAC_SIDE_MIRROR_HEAT = 339739916;
235 /**
236 * Steering Wheel Heating/Cooling
237 */
238 public static final int HVAC_STEERING_WHEEL_HEAT = 289408269;
239 /**
240 * Temperature units for display
241 */
242 public static final int HVAC_TEMPERATURE_DISPLAY_UNITS = 289408270;
243 /**
244 * Actual fan speed
245 */
246 public static final int HVAC_ACTUAL_FAN_SPEED_RPM = 356517135;
247 /**
248 * Represents global power state for HVAC. Setting this property to false
249 * MAY mark some properties that control individual HVAC features/subsystems
250 * to UNAVAILABLE state. Setting this property to true MAY mark some
251 * properties that control individual HVAC features/subsystems to AVAILABLE
252 * state (unless any/all of them are UNAVAILABLE on their own individual
253 * merits).
254 */
255 public static final int HVAC_POWER_ON = 354419984;
256 /**
257 * Fan Positions Available
258 */
259 public static final int HVAC_FAN_DIRECTION_AVAILABLE = 356582673;
260 /**
261 * Automatic recirculation on/off
262 */
263 public static final int HVAC_AUTO_RECIRC_ON = 354419986;
264 /**
265 * Seat ventilation
266 */
267 public static final int HVAC_SEAT_VENTILATION = 356517139;
268 /**
269 * Outside temperature
270 */
271 public static final int ENV_OUTSIDE_TEMPERATURE = 291505923;
272 /**
273 * Property to control power state of application processor
274 *
275 * It is assumed that AP's power state is controller by separate power
276 * controller.
277 */
278 public static final int AP_POWER_STATE_REQ = 289475072;
279 /**
280 * Property to report power state of application processor
281 *
282 * It is assumed that AP's power state is controller by separate power
283 * controller.
284 */
285 public static final int AP_POWER_STATE_REPORT = 289475073;
286 /**
287 * Property to report bootup reason for the current power on. This is a
288 * static property that will not change for the whole duration until power
289 * off. For example, even if user presses power on button after automatic
290 * power on with door unlock, bootup reason must stay with
291 * VehicleApPowerBootupReason#USER_UNLOCK.
292 */
293 public static final int AP_POWER_BOOTUP_REASON = 289409538;
294 /**
295 * Property to represent brightness of the display. Some cars have single
296 * control for the brightness of all displays and this property is to share
297 * change in that control.
298 */
299 public static final int DISPLAY_BRIGHTNESS = 289409539;
300 /**
301 * Property to feed H/W input events to android
302 */
303 public static final int HW_KEY_INPUT = 289475088;
304 /**
305 * Door position
306 *
307 * This is an integer in case a door may be set to a particular position.
308 * Max value indicates fully open, min value (0) indicates fully closed.
309 */
310 public static final int DOOR_POS = 373295872;
311 /**
312 * Door move
313 */
314 public static final int DOOR_MOVE = 373295873;
315 /**
316 * Door lock
317 */
318 public static final int DOOR_LOCK = 371198722;
319 /**
320 * Mirror Z Position
321 */
322 public static final int MIRROR_Z_POS = 339741504;
323 /**
324 * Mirror Z Move
325 */
326 public static final int MIRROR_Z_MOVE = 339741505;
327 /**
328 * Mirror Y Position
329 */
330 public static final int MIRROR_Y_POS = 339741506;
331 /**
332 * Mirror Y Move
333 */
334 public static final int MIRROR_Y_MOVE = 339741507;
335 /**
336 * Mirror Lock
337 */
338 public static final int MIRROR_LOCK = 287312708;
339 /**
340 * Mirror Fold
341 */
342 public static final int MIRROR_FOLD = 287312709;
343 /**
344 * Seat memory select
345 *
346 * This parameter selects the memory preset to use to select the seat
347 * position. The minValue is always 0, and the maxValue determines the
348 * number of seat positions available.
349 */
350 public static final int SEAT_MEMORY_SELECT = 356518784;
351 /**
352 * Seat memory set
353 *
354 * This setting allows the user to save the current seat position settings
355 * into the selected preset slot. The maxValue for each seat position
356 * must match the maxValue for SEAT_MEMORY_SELECT.
357 */
358 public static final int SEAT_MEMORY_SET = 356518785;
359 /**
360 * Seatbelt buckled
361 *
362 * True indicates belt is buckled.
363 */
364 public static final int SEAT_BELT_BUCKLED = 354421634;
365 /**
366 * Seatbelt height position
367 */
368 public static final int SEAT_BELT_HEIGHT_POS = 356518787;
369 /**
370 * Seatbelt height move
371 */
372 public static final int SEAT_BELT_HEIGHT_MOVE = 356518788;
373 /**
374 * Seat fore/aft position
375 */
376 public static final int SEAT_FORE_AFT_POS = 356518789;
377 /**
378 * Seat fore/aft move
379 */
380 public static final int SEAT_FORE_AFT_MOVE = 356518790;
381 /**
382 * Seat backrest angle 1 position
383 */
384 public static final int SEAT_BACKREST_ANGLE_1_POS = 356518791;
385 /**
386 * Seat backrest angle 1 move
387 *
388 * Moves the backrest forward or recline.
389 */
390 public static final int SEAT_BACKREST_ANGLE_1_MOVE = 356518792;
391 /**
392 * Seat backrest angle 2 position
393 */
394 public static final int SEAT_BACKREST_ANGLE_2_POS = 356518793;
395 /**
396 * Seat backrest angle 2 move
397 */
398 public static final int SEAT_BACKREST_ANGLE_2_MOVE = 356518794;
399 /**
400 * Seat height position
401 */
402 public static final int SEAT_HEIGHT_POS = 356518795;
403 /**
404 * Seat height move
405 */
406 public static final int SEAT_HEIGHT_MOVE = 356518796;
407 /**
408 * Seat depth position
409 */
410 public static final int SEAT_DEPTH_POS = 356518797;
411 /**
412 * Seat depth move
413 */
414 public static final int SEAT_DEPTH_MOVE = 356518798;
415 /**
416 * Seat tilt position
417 */
418 public static final int SEAT_TILT_POS = 356518799;
419 /**
420 * Seat tilt move
421 */
422 public static final int SEAT_TILT_MOVE = 356518800;
423 /**
424 * Lumber fore/aft position
425 */
426 public static final int SEAT_LUMBAR_FORE_AFT_POS = 356518801;
427 /**
428 * Lumbar fore/aft move
429 */
430 public static final int SEAT_LUMBAR_FORE_AFT_MOVE = 356518802;
431 /**
432 * Lumbar side support position
433 */
434 public static final int SEAT_LUMBAR_SIDE_SUPPORT_POS = 356518803;
435 /**
436 * Lumbar side support move
437 */
438 public static final int SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 356518804;
439 /**
440 * Headrest height position
441 */
442 public static final int SEAT_HEADREST_HEIGHT_POS = 289409941;
443 /**
444 * Headrest height move
445 */
446 public static final int SEAT_HEADREST_HEIGHT_MOVE = 356518806;
447 /**
448 * Headrest angle position
449 */
450 public static final int SEAT_HEADREST_ANGLE_POS = 356518807;
451 /**
452 * Headrest angle move
453 */
454 public static final int SEAT_HEADREST_ANGLE_MOVE = 356518808;
455 /**
456 * Headrest fore/aft position
457 */
458 public static final int SEAT_HEADREST_FORE_AFT_POS = 356518809;
459 /**
460 * Headrest fore/aft move
461 */
462 public static final int SEAT_HEADREST_FORE_AFT_MOVE = 356518810;
463 /**
464 * Window Position
465 */
466 public static final int WINDOW_POS = 322964416;
467 /**
468 * Window Move
469 */
470 public static final int WINDOW_MOVE = 322964417;
471 /**
472 * Window Lock
473 */
474 public static final int WINDOW_LOCK = 320867268;
475 /**
476 * Vehicle Maps Service (VMS) message
477 */
478 public static final int VEHICLE_MAP_SERVICE = 299895808;
479 /**
480 * OBD2 Live Sensor Data
481 *
482 * Reports a snapshot of the current (live) values of the OBD2 sensors available.
483 */
484 public static final int OBD2_LIVE_FRAME = 299896064;
485 /**
486 * OBD2 Freeze Frame Sensor Data
487 *
488 * Reports a snapshot of the value of the OBD2 sensors available at the time that a fault
489 * occurred and was detected.
490 */
491 public static final int OBD2_FREEZE_FRAME = 299896065;
492 /**
493 * OBD2 Freeze Frame Information
494 */
495 public static final int OBD2_FREEZE_FRAME_INFO = 299896066;
496 /**
497 * OBD2 Freeze Frame Clear
498 *
499 * This property allows deletion of any of the freeze frames stored in
500 * vehicle memory, as described by OBD2_FREEZE_FRAME_INFO.
501 */
502 public static final int OBD2_FREEZE_FRAME_CLEAR = 299896067;
503 /**
504 * Headlights State
505 */
506 public static final int HEADLIGHTS_STATE = 289410560;
507 /**
508 * High beam lights state
509 */
510 public static final int HIGH_BEAM_LIGHTS_STATE = 289410561;
511 /**
512 * Fog light state
513 */
514 public static final int FOG_LIGHTS_STATE = 289410562;
515 /**
516 * Hazard light status
517 */
518 public static final int HAZARD_LIGHTS_STATE = 289410563;
519 /**
520 * Headlight switch
521 */
522 public static final int HEADLIGHTS_SWITCH = 289410576;
523 /**
524 * High beam light switch
525 */
526 public static final int HIGH_BEAM_LIGHTS_SWITCH = 289410577;
527 /**
528 * Fog light switch
529 */
530 public static final int FOG_LIGHTS_SWITCH = 289410578;
531 /**
532 * Hazard light switch
533 */
534 public static final int HAZARD_LIGHTS_SWITCH = 289410579;
535
536 /**
537 * @param o Integer
538 * @return String
539 */
540 public static String toString(int o) {
541 if (o == INVALID) {
542 return "INVALID";
543 }
544 if (o == INFO_VIN) {
545 return "INFO_VIN";
546 }
547 if (o == INFO_MAKE) {
548 return "INFO_MAKE";
549 }
550 if (o == INFO_MODEL) {
551 return "INFO_MODEL";
552 }
553 if (o == INFO_MODEL_YEAR) {
554 return "INFO_MODEL_YEAR";
555 }
556 if (o == INFO_FUEL_CAPACITY) {
557 return "INFO_FUEL_CAPACITY";
558 }
559 if (o == INFO_FUEL_TYPE) {
560 return "INFO_FUEL_TYPE";
561 }
562 if (o == INFO_EV_BATTERY_CAPACITY) {
563 return "INFO_EV_BATTERY_CAPACITY";
564 }
565 if (o == INFO_EV_CONNECTOR_TYPE) {
566 return "INFO_EV_CONNECTOR_TYPE";
567 }
568 if (o == INFO_FUEL_DOOR_LOCATION) {
569 return "INFO_FUEL_DOOR_LOCATION";
570 }
571 if (o == INFO_EV_PORT_LOCATION) {
572 return "INFO_EV_PORT_LOCATION";
573 }
574 if (o == INFO_DRIVER_SEAT) {
575 return "INFO_DRIVER_SEAT";
576 }
577 if (o == PERF_ODOMETER) {
578 return "PERF_ODOMETER";
579 }
580 if (o == PERF_VEHICLE_SPEED) {
581 return "PERF_VEHICLE_SPEED";
582 }
583 if (o == ENGINE_COOLANT_TEMP) {
584 return "ENGINE_COOLANT_TEMP";
585 }
586 if (o == ENGINE_OIL_LEVEL) {
587 return "ENGINE_OIL_LEVEL";
588 }
589 if (o == ENGINE_OIL_TEMP) {
590 return "ENGINE_OIL_TEMP";
591 }
592 if (o == ENGINE_RPM) {
593 return "ENGINE_RPM";
594 }
595 if (o == WHEEL_TICK) {
596 return "WHEEL_TICK";
597 }
598 if (o == FUEL_LEVEL) {
599 return "FUEL_LEVEL";
600 }
601 if (o == FUEL_DOOR_OPEN) {
602 return "FUEL_DOOR_OPEN";
603 }
604 if (o == EV_BATTERY_LEVEL) {
605 return "EV_BATTERY_LEVEL";
606 }
607 if (o == EV_CHARGE_PORT_OPEN) {
608 return "EV_CHARGE_PORT_OPEN";
609 }
610 if (o == EV_CHARGE_PORT_CONNECTED) {
611 return "EV_CHARGE_PORT_CONNECTED";
612 }
613 if (o == EV_BATTERY_INSTANTANEOUS_CHARGE_RATE) {
614 return "EV_BATTERY_INSTANTANEOUS_CHARGE_RATE";
615 }
616 if (o == RANGE_REMAINING) {
617 return "RANGE_REMAINING";
618 }
619 if (o == TIRE_PRESSURE) {
620 return "TIRE_PRESSURE";
621 }
622 if (o == GEAR_SELECTION) {
623 return "GEAR_SELECTION";
624 }
625 if (o == CURRENT_GEAR) {
626 return "CURRENT_GEAR";
627 }
628 if (o == PARKING_BRAKE_ON) {
629 return "PARKING_BRAKE_ON";
630 }
631 if (o == PARKING_BRAKE_AUTO_APPLY) {
632 return "PARKING_BRAKE_AUTO_APPLY";
633 }
634 if (o == FUEL_LEVEL_LOW) {
635 return "FUEL_LEVEL_LOW";
636 }
637 if (o == NIGHT_MODE) {
638 return "NIGHT_MODE";
639 }
640 if (o == TURN_SIGNAL_STATE) {
641 return "TURN_SIGNAL_STATE";
642 }
643 if (o == IGNITION_STATE) {
644 return "IGNITION_STATE";
645 }
646 if (o == ABS_ACTIVE) {
647 return "ABS_ACTIVE";
648 }
649 if (o == TRACTION_CONTROL_ACTIVE) {
650 return "TRACTION_CONTROL_ACTIVE";
651 }
652 if (o == HVAC_FAN_SPEED) {
653 return "HVAC_FAN_SPEED";
654 }
655 if (o == HVAC_FAN_DIRECTION) {
656 return "HVAC_FAN_DIRECTION";
657 }
658 if (o == HVAC_TEMPERATURE_CURRENT) {
659 return "HVAC_TEMPERATURE_CURRENT";
660 }
661 if (o == HVAC_TEMPERATURE_SET) {
662 return "HVAC_TEMPERATURE_SET";
663 }
664 if (o == HVAC_DEFROSTER) {
665 return "HVAC_DEFROSTER";
666 }
667 if (o == HVAC_AC_ON) {
668 return "HVAC_AC_ON";
669 }
670 if (o == HVAC_MAX_AC_ON) {
671 return "HVAC_MAX_AC_ON";
672 }
673 if (o == HVAC_MAX_DEFROST_ON) {
674 return "HVAC_MAX_DEFROST_ON";
675 }
676 if (o == HVAC_RECIRC_ON) {
677 return "HVAC_RECIRC_ON";
678 }
679 if (o == HVAC_DUAL_ON) {
680 return "HVAC_DUAL_ON";
681 }
682 if (o == HVAC_AUTO_ON) {
683 return "HVAC_AUTO_ON";
684 }
685 if (o == HVAC_SEAT_TEMPERATURE) {
686 return "HVAC_SEAT_TEMPERATURE";
687 }
688 if (o == HVAC_SIDE_MIRROR_HEAT) {
689 return "HVAC_SIDE_MIRROR_HEAT";
690 }
691 if (o == HVAC_STEERING_WHEEL_HEAT) {
692 return "HVAC_STEERING_WHEEL_HEAT";
693 }
694 if (o == HVAC_TEMPERATURE_DISPLAY_UNITS) {
695 return "HVAC_TEMPERATURE_DISPLAY_UNITS";
696 }
697 if (o == HVAC_ACTUAL_FAN_SPEED_RPM) {
698 return "HVAC_ACTUAL_FAN_SPEED_RPM";
699 }
700 if (o == HVAC_POWER_ON) {
701 return "HVAC_POWER_ON";
702 }
703 if (o == HVAC_FAN_DIRECTION_AVAILABLE) {
704 return "HVAC_FAN_DIRECTION_AVAILABLE";
705 }
706 if (o == HVAC_AUTO_RECIRC_ON) {
707 return "HVAC_AUTO_RECIRC_ON";
708 }
709 if (o == HVAC_SEAT_VENTILATION) {
710 return "HVAC_SEAT_VENTILATION";
711 }
712 if (o == ENV_OUTSIDE_TEMPERATURE) {
713 return "ENV_OUTSIDE_TEMPERATURE";
714 }
715 if (o == AP_POWER_STATE_REQ) {
716 return "AP_POWER_STATE_REQ";
717 }
718 if (o == AP_POWER_STATE_REPORT) {
719 return "AP_POWER_STATE_REPORT";
720 }
721 if (o == AP_POWER_BOOTUP_REASON) {
722 return "AP_POWER_BOOTUP_REASON";
723 }
724 if (o == DISPLAY_BRIGHTNESS) {
725 return "DISPLAY_BRIGHTNESS";
726 }
727 if (o == HW_KEY_INPUT) {
728 return "HW_KEY_INPUT";
729 }
730 if (o == DOOR_POS) {
731 return "DOOR_POS";
732 }
733 if (o == DOOR_MOVE) {
734 return "DOOR_MOVE";
735 }
736 if (o == DOOR_LOCK) {
737 return "DOOR_LOCK";
738 }
739 if (o == MIRROR_Z_POS) {
740 return "MIRROR_Z_POS";
741 }
742 if (o == MIRROR_Z_MOVE) {
743 return "MIRROR_Z_MOVE";
744 }
745 if (o == MIRROR_Y_POS) {
746 return "MIRROR_Y_POS";
747 }
748 if (o == MIRROR_Y_MOVE) {
749 return "MIRROR_Y_MOVE";
750 }
751 if (o == MIRROR_LOCK) {
752 return "MIRROR_LOCK";
753 }
754 if (o == MIRROR_FOLD) {
755 return "MIRROR_FOLD";
756 }
757 if (o == SEAT_MEMORY_SELECT) {
758 return "SEAT_MEMORY_SELECT";
759 }
760 if (o == SEAT_MEMORY_SET) {
761 return "SEAT_MEMORY_SET";
762 }
763 if (o == SEAT_BELT_BUCKLED) {
764 return "SEAT_BELT_BUCKLED";
765 }
766 if (o == SEAT_BELT_HEIGHT_POS) {
767 return "SEAT_BELT_HEIGHT_POS";
768 }
769 if (o == SEAT_BELT_HEIGHT_MOVE) {
770 return "SEAT_BELT_HEIGHT_MOVE";
771 }
772 if (o == SEAT_FORE_AFT_POS) {
773 return "SEAT_FORE_AFT_POS";
774 }
775 if (o == SEAT_FORE_AFT_MOVE) {
776 return "SEAT_FORE_AFT_MOVE";
777 }
778 if (o == SEAT_BACKREST_ANGLE_1_POS) {
779 return "SEAT_BACKREST_ANGLE_1_POS";
780 }
781 if (o == SEAT_BACKREST_ANGLE_1_MOVE) {
782 return "SEAT_BACKREST_ANGLE_1_MOVE";
783 }
784 if (o == SEAT_BACKREST_ANGLE_2_POS) {
785 return "SEAT_BACKREST_ANGLE_2_POS";
786 }
787 if (o == SEAT_BACKREST_ANGLE_2_MOVE) {
788 return "SEAT_BACKREST_ANGLE_2_MOVE";
789 }
790 if (o == SEAT_HEIGHT_POS) {
791 return "SEAT_HEIGHT_POS";
792 }
793 if (o == SEAT_HEIGHT_MOVE) {
794 return "SEAT_HEIGHT_MOVE";
795 }
796 if (o == SEAT_DEPTH_POS) {
797 return "SEAT_DEPTH_POS";
798 }
799 if (o == SEAT_DEPTH_MOVE) {
800 return "SEAT_DEPTH_MOVE";
801 }
802 if (o == SEAT_TILT_POS) {
803 return "SEAT_TILT_POS";
804 }
805 if (o == SEAT_TILT_MOVE) {
806 return "SEAT_TILT_MOVE";
807 }
808 if (o == SEAT_LUMBAR_FORE_AFT_POS) {
809 return "SEAT_LUMBAR_FORE_AFT_POS";
810 }
811 if (o == SEAT_LUMBAR_FORE_AFT_MOVE) {
812 return "SEAT_LUMBAR_FORE_AFT_MOVE";
813 }
814 if (o == SEAT_LUMBAR_SIDE_SUPPORT_POS) {
815 return "SEAT_LUMBAR_SIDE_SUPPORT_POS";
816 }
817 if (o == SEAT_LUMBAR_SIDE_SUPPORT_MOVE) {
818 return "SEAT_LUMBAR_SIDE_SUPPORT_MOVE";
819 }
820 if (o == SEAT_HEADREST_HEIGHT_POS) {
821 return "SEAT_HEADREST_HEIGHT_POS";
822 }
823 if (o == SEAT_HEADREST_HEIGHT_MOVE) {
824 return "SEAT_HEADREST_HEIGHT_MOVE";
825 }
826 if (o == SEAT_HEADREST_ANGLE_POS) {
827 return "SEAT_HEADREST_ANGLE_POS";
828 }
829 if (o == SEAT_HEADREST_ANGLE_MOVE) {
830 return "SEAT_HEADREST_ANGLE_MOVE";
831 }
832 if (o == SEAT_HEADREST_FORE_AFT_POS) {
833 return "SEAT_HEADREST_FORE_AFT_POS";
834 }
835 if (o == SEAT_HEADREST_FORE_AFT_MOVE) {
836 return "SEAT_HEADREST_FORE_AFT_MOVE";
837 }
838 if (o == WINDOW_POS) {
839 return "WINDOW_POS";
840 }
841 if (o == WINDOW_MOVE) {
842 return "WINDOW_MOVE";
843 }
844 if (o == WINDOW_LOCK) {
845 return "WINDOW_LOCK";
846 }
847 if (o == VEHICLE_MAP_SERVICE) {
848 return "VEHICLE_MAP_SERVICE";
849 }
850 if (o == OBD2_LIVE_FRAME) {
851 return "OBD2_LIVE_FRAME";
852 }
853 if (o == OBD2_FREEZE_FRAME) {
854 return "OBD2_FREEZE_FRAME";
855 }
856 if (o == OBD2_FREEZE_FRAME_INFO) {
857 return "OBD2_FREEZE_FRAME_INFO";
858 }
859 if (o == OBD2_FREEZE_FRAME_CLEAR) {
860 return "OBD2_FREEZE_FRAME_CLEAR";
861 }
862 if (o == HEADLIGHTS_STATE) {
863 return "HEADLIGHTS_STATE";
864 }
865 if (o == HIGH_BEAM_LIGHTS_STATE) {
866 return "HIGH_BEAM_LIGHTS_STATE";
867 }
868 if (o == FOG_LIGHTS_STATE) {
869 return "FOG_LIGHTS_STATE";
870 }
871 if (o == HAZARD_LIGHTS_STATE) {
872 return "HAZARD_LIGHTS_STATE";
873 }
874 if (o == HEADLIGHTS_SWITCH) {
875 return "HEADLIGHTS_SWITCH";
876 }
877 if (o == HIGH_BEAM_LIGHTS_SWITCH) {
878 return "HIGH_BEAM_LIGHTS_SWITCH";
879 }
880 if (o == FOG_LIGHTS_SWITCH) {
881 return "FOG_LIGHTS_SWITCH";
882 }
883 if (o == HAZARD_LIGHTS_SWITCH) {
884 return "HAZARD_LIGHTS_SWITCH";
885 }
886 return "0x" + Integer.toHexString(o);
887 }
888}
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictions.java b/car-lib/src/android/car/drivingstate/CarUxRestrictions.java
index c6c22bc3..87dfa3aa 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictions.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictions.java
@@ -24,30 +24,32 @@ import java.lang.annotation.Retention;
24import java.lang.annotation.RetentionPolicy; 24import java.lang.annotation.RetentionPolicy;
25 25
26/** 26/**
27 * Car UX Restrictions event. This contains information on the set of UX restrictions 27 * Car UX Restrictions event. This contains information on the set of UX restrictions that is in
28 * that is in place due to the car's driving state. 28 * place due to the car's driving state.
29 * <p> 29 * <p>
30 * The restriction information is organized as follows: 30 * The restriction information is organized as follows:
31 * <ul> 31 * <ul>
32 * <li> When there are no restrictions in place, for example when the car is parked, 32 * <li> When there are no restrictions in place, for example when the car is parked,
33 * <ul> 33 * <ul>
34 * <li> {@link #mRequiresDistractionOptimization} is set to false. Apps can display activities 34 * <li> {@link #isRequiresDistractionOptimization()} returns false. Apps can display activities
35 * that are not distraction optimized. 35 * that are not distraction optimized.
36 * <li> {@link #mActiveRestrictions} should contain UX_RESTRICTIONS_UNRESTRICTED. Apps don't 36 * <li> When {@link #isRequiresDistractionOptimization()} returns false, apps don't have to call
37 * have to check for this since {@code mRequiresDistractionOptimization} is false. 37 * {@link #getActiveRestrictions()}, since there is no distraction optimization required.
38 * </ul> 38 * </ul>
39 * <li> When the driving state changes, causing the UX restrictions to come in effect, 39 * <li> When the driving state changes, causing the UX restrictions to come in effect,
40 * <ul> 40 * <ul>
41 * <li> {@code mRequiresDistractionOptimization} is set to true. Apps can only display 41 * <li> {@link #isRequiresDistractionOptimization()} returns true. Apps can only display activities
42 * activities that are distraction optimized. Distraction optimized activities follow the base 42 * that are distraction optimized. Distraction optimized activities must follow the base design
43 * design guidelines that provide a distraction free driving user experience. 43 * guidelines to ensure a distraction free driving experience for the user.
44 * <li> In addition, apps will have to check for the content of mActiveRestrictions. 44 * <li> When {@link #isRequiresDistractionOptimization()} returns true, apps must call
45 * {@code mActiveRestrictions} will have additional granular information on the set of UX 45 * {@link #getActiveRestrictions()}, to get the currently active UX restrictions to adhere to.
46 * restrictions that are in place for the current driving state. The content of 46 * {@link #getActiveRestrictions()} provides additional information on the set of UX
47 * {@code mActiveRestrictions}, for the same driving state of the vehicle, could vary depending 47 * restrictions that are in place for the current driving state.
48 * on the car maker and the market. For example, when the car is idling, the set of active 48 * <p>
49 * UX restrictions contained in the {@code mActiveRestrictions} will depend on the car maker 49 * The UX restrictions returned by {@link #getActiveRestrictions()}, for the same driving state of
50 * and the safety standards of the market that the vehicle is deployed in. 50 * the vehicle, could vary depending on the OEM and the market. For example, when the car is
51 * idling, the set of active UX restrictions will depend on the car maker and the safety standards
52 * of the market that the vehicle is deployed in.
51 * </ul> 53 * </ul>
52 * </ul> 54 * </ul>
53 * <p> 55 * <p>
diff --git a/car-lib/src/android/car/hardware/CarSensorEvent.java b/car-lib/src/android/car/hardware/CarSensorEvent.java
index 86af0637..8cba0a27 100644
--- a/car-lib/src/android/car/hardware/CarSensorEvent.java
+++ b/car-lib/src/android/car/hardware/CarSensorEvent.java
@@ -92,15 +92,11 @@ public class CarSensorEvent implements Parcelable {
92 public static final int IGNITION_STATE_START = 5; 92 public static final int IGNITION_STATE_START = 5;
93 93
94 /** 94 /**
95 * Index for {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT} in floatValues. 95 * Index for {@link CarSensorManager#SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE} in floatValues.
96 * Temperature in Celsius degrees. 96 * Temperature in Celsius degrees.
97 */ 97 */
98 public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0; 98 public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0;
99 /** 99
100 * Index for {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT} in floatValues.
101 * Pressure in kPa.
102 */
103 public static final int INDEX_ENVIRONMENT_PRESSURE = 1;
104 /** 100 /**
105 * Index for {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE} in longValues. RESET_COUNT 101 * Index for {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE} in longValues. RESET_COUNT
106 * is incremented whenever the HAL detects that a sensor reset has occurred. It represents to 102 * is incremented whenever the HAL detects that a sensor reset has occurred. It represents to
@@ -208,8 +204,6 @@ public class CarSensorEvent implements Parcelable {
208 public long timestamp; 204 public long timestamp;
209 /** If unsupported by the car, this value is NaN. */ 205 /** If unsupported by the car, this value is NaN. */
210 public float temperature; 206 public float temperature;
211 /** If unsupported by the car, this value is NaN. */
212 public float pressure;
213 207
214 /** @hide */ 208 /** @hide */
215 private EnvironmentData() {}; 209 private EnvironmentData() {};
@@ -217,7 +211,7 @@ public class CarSensorEvent implements Parcelable {
217 211
218 /** 212 /**
219 * Convenience method for obtaining an {@link EnvironmentData} object from a CarSensorEvent 213 * Convenience method for obtaining an {@link EnvironmentData} object from a CarSensorEvent
220 * object with type {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT}. 214 * object with type {@link CarSensorManager#SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE}.
221 * 215 *
222 * @param data an optional output parameter which, if non-null, will be used by this method 216 * @param data an optional output parameter which, if non-null, will be used by this method
223 * instead of a newly created object. 217 * instead of a newly created object.
@@ -225,13 +219,40 @@ public class CarSensorEvent implements Parcelable {
225 * @hide 219 * @hide
226 */ 220 */
227 public EnvironmentData getEnvironmentData(EnvironmentData data) { 221 public EnvironmentData getEnvironmentData(EnvironmentData data) {
228 checkType(CarSensorManager.SENSOR_TYPE_ENVIRONMENT); 222 checkType(CarSensorManager.SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE);
229 if (data == null) { 223 if (data == null) {
230 data = new EnvironmentData(); 224 data = new EnvironmentData();
231 } 225 }
232 data.timestamp = timestamp; 226 data.timestamp = timestamp;
233 data.temperature = floatValues[INDEX_ENVIRONMENT_TEMPERATURE]; 227 data.temperature = floatValues[INDEX_ENVIRONMENT_TEMPERATURE];
234 data.pressure = floatValues[INDEX_ENVIRONMENT_PRESSURE]; 228 return data;
229 }
230
231 /** @hide*/
232 public static class IgnitionStateData {
233 public long timestamp;
234 public int ignitionState;
235
236 /** @hide */
237 private IgnitionStateData() {};
238 }
239
240 /**
241 * Convenience method for obtaining a {@link IgnitionStateData} object from a CarSensorEvent
242 * object with type {@link CarSensorManager#SENSOR_TYPE_IGNITION_STATE}.
243 *
244 * @param data an optional output parameter which, if non-null, will be used by this method
245 * instead of a newly created object.
246 * @return a IgnitionStateData object corresponding to the data contained in the CarSensorEvent.
247 * @hide
248 */
249 public IgnitionStateData getIgnitionStateData(IgnitionStateData data) {
250 checkType(CarSensorManager.SENSOR_TYPE_IGNITION_STATE);
251 if (data == null) {
252 data = new IgnitionStateData();
253 }
254 data.timestamp = timestamp;
255 data.ignitionState = intValues[0];
235 return data; 256 return data;
236 } 257 }
237 258
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index 25366046..338d74f4 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -91,14 +91,17 @@ public final class CarSensorManager implements CarManagerBase {
91 * Day/night sensor. Sensor data is intValues[0]. 91 * Day/night sensor. Sensor data is intValues[0].
92 */ 92 */
93 public static final int SENSOR_TYPE_NIGHT = 0x11200407; 93 public static final int SENSOR_TYPE_NIGHT = 0x11200407;
94 /**
95 * Outside Environment like temperature.
96 * This requires {@link Car#PERMISSION_EXTERIOR_ENVIRONMENT} permission.
97 */
98 public static final int SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE = 0x11600703;
94 /** @hide */ 99 /** @hide */
95 public static final int SENSOR_TYPE_RESERVED10 = 10; 100 public static final int SENSOR_TYPE_RESERVED10 = 10;
96 /** @hide */ 101 /** @hide */
97 public static final int SENSOR_TYPE_RESERVED11 = 11; 102 public static final int SENSOR_TYPE_RESERVED11 = 11;
98 /** 103 /** @hide */
99 * Environment like temperature and pressure. 104 public static final int SENSOR_TYPE_RESERVED12 = 12;
100 */
101 public static final int SENSOR_TYPE_ENVIRONMENT = 12;
102 /** @hide */ 105 /** @hide */
103 public static final int SENSOR_TYPE_RESERVED13 = 13; 106 public static final int SENSOR_TYPE_RESERVED13 = 13;
104 /** @hide */ 107 /** @hide */
@@ -186,7 +189,7 @@ public final class CarSensorManager implements CarManagerBase {
186 SENSOR_TYPE_PARKING_BRAKE, 189 SENSOR_TYPE_PARKING_BRAKE,
187 SENSOR_TYPE_GEAR, 190 SENSOR_TYPE_GEAR,
188 SENSOR_TYPE_NIGHT, 191 SENSOR_TYPE_NIGHT,
189 SENSOR_TYPE_ENVIRONMENT, 192 SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE,
190 SENSOR_TYPE_IGNITION_STATE, 193 SENSOR_TYPE_IGNITION_STATE,
191 SENSOR_TYPE_WHEEL_TICK_DISTANCE, 194 SENSOR_TYPE_WHEEL_TICK_DISTANCE,
192 SENSOR_TYPE_ABS_ACTIVE, 195 SENSOR_TYPE_ABS_ACTIVE,
@@ -209,7 +212,7 @@ public final class CarSensorManager implements CarManagerBase {
209 SENSOR_TYPE_PARKING_BRAKE, 212 SENSOR_TYPE_PARKING_BRAKE,
210 SENSOR_TYPE_GEAR, 213 SENSOR_TYPE_GEAR,
211 SENSOR_TYPE_NIGHT, 214 SENSOR_TYPE_NIGHT,
212 SENSOR_TYPE_ENVIRONMENT, 215 SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE,
213 SENSOR_TYPE_IGNITION_STATE, 216 SENSOR_TYPE_IGNITION_STATE,
214 SENSOR_TYPE_WHEEL_TICK_DISTANCE, 217 SENSOR_TYPE_WHEEL_TICK_DISTANCE,
215 SENSOR_TYPE_ABS_ACTIVE, 218 SENSOR_TYPE_ABS_ACTIVE,
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
index 5c094e75..373c23a7 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyManager.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -18,6 +18,7 @@ package android.car.hardware.property;
18 18
19import static java.lang.Integer.toHexString; 19import static java.lang.Integer.toHexString;
20 20
21import android.annotation.Nullable;
21import android.car.CarApiUtil; 22import android.car.CarApiUtil;
22import android.car.CarManagerBase; 23import android.car.CarManagerBase;
23import android.car.CarNotConnectedException; 24import android.car.CarNotConnectedException;
@@ -44,6 +45,7 @@ import java.util.function.Consumer;
44 * @hide 45 * @hide
45 */ 46 */
46public class CarPropertyManager implements CarManagerBase { 47public class CarPropertyManager implements CarManagerBase {
48 private final List<CarPropertyConfig> mConfigs;
47 private final boolean mDbg; 49 private final boolean mDbg;
48 private final SingleMessageHandler<CarPropertyEvent> mHandler; 50 private final SingleMessageHandler<CarPropertyEvent> mHandler;
49 private final ICarProperty mService; 51 private final ICarProperty mService;
@@ -69,10 +71,20 @@ public class CarPropertyManager implements CarManagerBase {
69 /** 71 /**
70 * Get an instance of the CarPropertyManager. 72 * Get an instance of the CarPropertyManager.
71 */ 73 */
72 public CarPropertyManager(IBinder service, Handler handler, boolean dbg, String tag) { 74 public CarPropertyManager(IBinder service, @Nullable Handler handler, boolean dbg, String tag) {
73 mDbg = dbg; 75 mDbg = dbg;
74 mTag = tag; 76 mTag = tag;
75 mService = ICarProperty.Stub.asInterface(service); 77 mService = ICarProperty.Stub.asInterface(service);
78 try {
79 mConfigs = mService.getPropertyList();
80 } catch (Exception e) {
81 Log.e(mTag, "getPropertyList exception ", e);
82 throw new RuntimeException(e);
83 }
84 if (handler == null) {
85 mHandler = null;
86 return;
87 }
76 mHandler = new SingleMessageHandler<CarPropertyEvent>(handler.getLooper(), 88 mHandler = new SingleMessageHandler<CarPropertyEvent>(handler.getLooper(),
77 MSG_GENERIC_EVENT) { 89 MSG_GENERIC_EVENT) {
78 @Override 90 @Override
@@ -154,7 +166,9 @@ public class CarPropertyManager implements CarManagerBase {
154 } 166 }
155 167
156 private void handleEvent(List<CarPropertyEvent> events) { 168 private void handleEvent(List<CarPropertyEvent> events) {
157 mHandler.sendEvents(events); 169 if (mHandler != null) {
170 mHandler.sendEvents(events);
171 }
158 } 172 }
159 173
160 /** 174 /**
@@ -164,8 +178,12 @@ public class CarPropertyManager implements CarManagerBase {
164 */ 178 */
165 public void unregisterListener(CarPropertyEventListener listener) { 179 public void unregisterListener(CarPropertyEventListener listener) {
166 synchronized (mActivePropertyListener) { 180 synchronized (mActivePropertyListener) {
181 int [] propertyIds = new int[mActivePropertyListener.size()];
167 for (int i = 0; i < mActivePropertyListener.size(); i++) { 182 for (int i = 0; i < mActivePropertyListener.size(); i++) {
168 doUnregisterListenerLocked(listener, mActivePropertyListener.keyAt(i)); 183 propertyIds[i] = mActivePropertyListener.keyAt(i);
184 }
185 for (int prop : propertyIds) {
186 doUnregisterListenerLocked(listener, prop);
169 } 187 }
170 } 188 }
171 } 189 }
@@ -207,41 +225,24 @@ public class CarPropertyManager implements CarManagerBase {
207 } 225 }
208 226
209 /** 227 /**
210 * Returns the list of properties implemented by this car. 228 * @return List of properties implemented by this car that the application may access.
211 *
212 * @return Caller must check the property type and typecast to the appropriate subclass
213 * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
214 */ 229 */
215 public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException { 230 public List<CarPropertyConfig> getPropertyList() {
216 try { 231 return mConfigs;
217 return mService.getPropertyList();
218 } catch (RemoteException e) {
219 Log.e(mTag, "getPropertyList exception ", e);
220 throw new CarNotConnectedException(e);
221 }
222 } 232 }
223 233
224 /** 234 /**
225 * Returns the list of properties implemented by this car in given property id list. 235 * @return List of properties implemented by this car in given property ID list that application
226 * 236 * may access.
227 * @return Caller must check the property type and typecast to the appropriate subclass
228 * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
229 */ 237 */
230 public List<CarPropertyConfig> getPropertyList(ArraySet<Integer> propertyIds) 238 public List<CarPropertyConfig> getPropertyList(ArraySet<Integer> propertyIds) {
231 throws CarNotConnectedException { 239 List<CarPropertyConfig> configs = new ArrayList<>();
232 try { 240 for (CarPropertyConfig c : mConfigs) {
233 List<CarPropertyConfig> configs = new ArrayList<>(); 241 if (propertyIds.contains(c.getPropertyId())) {
234 for (CarPropertyConfig c : mService.getPropertyList()) { 242 configs.add(c);
235 if (propertyIds.contains(c.getPropertyId())) {
236 configs.add(c);
237 }
238 } 243 }
239 return configs;
240 } catch (RemoteException e) {
241 Log.e(mTag, "getPropertyList exception ", e);
242 throw new CarNotConnectedException(e);
243 } 244 }
244 245 return configs;
245 } 246 }
246 247
247 /** 248 /**
@@ -294,6 +295,26 @@ public class CarPropertyManager implements CarManagerBase {
294 return carProp != null ? carProp.getValue() : 0; 295 return carProp != null ? carProp.getValue() : 0;
295 } 296 }
296 297
298 /**
299 * Returns value of a integer array property
300 *
301 * @param prop Property ID to get
302 * @param area Zone of the property to get
303 */
304 public int[] getIntArrayProperty(int prop, int area) throws CarNotConnectedException {
305 CarPropertyValue<Integer[]> carProp = getProperty(Integer[].class, prop, area);
306 return carProp != null ? toIntArray(carProp.getValue()) : new int[0];
307 }
308
309 private static int[] toIntArray(Integer[] input) {
310 int len = input.length;
311 int[] arr = new int[len];
312 for (int i = 0; i < len; i++) {
313 arr[i] = input[i];
314 }
315 return arr;
316 }
317
297 /** Return CarPropertyValue */ 318 /** Return CarPropertyValue */
298 @SuppressWarnings("unchecked") 319 @SuppressWarnings("unchecked")
299 public <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area) 320 public <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area)
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index c9aef749..7257255c 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -47,6 +47,13 @@ public final class CarAudioManager implements CarManagerBase {
47 return VOLUME_SETTINGS_KEY_FOR_GROUP_PREFIX + groupId; 47 return VOLUME_SETTINGS_KEY_FOR_GROUP_PREFIX + groupId;
48 } 48 }
49 49
50 /**
51 * Key to persist master mute state in {@link Settings.Global}
52 *
53 * @hide
54 */
55 public static final String VOLUME_SETTINGS_KEY_MASTER_MUTE = "android.car.MASTER_MUTE";
56
50 private final ContentResolver mContentResolver; 57 private final ContentResolver mContentResolver;
51 private final ICarAudio mService; 58 private final ICarAudio mService;
52 59
diff --git a/car-lib/src/android/car/settings/CarSettings.java b/car-lib/src/android/car/settings/CarSettings.java
index d7402b26..f5e10378 100644
--- a/car-lib/src/android/car/settings/CarSettings.java
+++ b/car-lib/src/android/car/settings/CarSettings.java
@@ -45,6 +45,22 @@ public class CarSettings {
45 */ 45 */
46 public static final String KEY_GARAGE_MODE_MAINTENANCE_WINDOW = 46 public static final String KEY_GARAGE_MODE_MAINTENANCE_WINDOW =
47 "android.car.GARAGE_MODE_MAINTENANCE_WINDOW"; 47 "android.car.GARAGE_MODE_MAINTENANCE_WINDOW";
48
49 /**
50 * Key for default user id to boot into.
51 *
52 * @hide
53 */
54 public static final String DEFAULT_USER_ID_TO_BOOT_INTO =
55 "android.car.DEFAULT_BOOT_INTO_USER_ID";
56
57 /**
58 * Key for user id that is last logged in to.
59 *
60 * @hide
61 */
62 public static final String LAST_ACTIVE_USER_ID =
63 "android.car.LAST_ACTIVE_USER_ID";
48 } 64 }
49 65
50 /** 66 /**
@@ -62,20 +78,6 @@ public class CarSettings {
62 public static final int DEFAULT_GARAGE_MODE_MAINTENANCE_WINDOW = 10 * 60 * 1000; // 10 mins 78 public static final int DEFAULT_GARAGE_MODE_MAINTENANCE_WINDOW = 10 * 60 * 1000; // 10 mins
63 79
64 /** 80 /**
65 * Id for user that is set as default to boot into.
66 *
67 * @hide
68 */
69 public static final int DEFAULT_USER_ID_TO_BOOT_INTO = 10; // Default to first created user.
70
71 /**
72 * Id for user that is last logged in to.
73 *
74 * @hide
75 */
76 public static final int LAST_ACTIVE_USER_ID = 10; // Default to first created user.
77
78 /**
79 * @hide 81 * @hide
80 */ 82 */
81 public static final class Secure { 83 public static final class Secure {
diff --git a/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl b/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl
index 8b50fd37..4dec6a0a 100644
--- a/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl
+++ b/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl
@@ -58,4 +58,7 @@ interface ICarTrustAgentBleService {
58 void onEscrowTokenAdded(in byte[] token, long handle, int uid); 58 void onEscrowTokenAdded(in byte[] token, long handle, int uid);
59 void onEscrowTokenRemoved(long handle, boolean successful); 59 void onEscrowTokenRemoved(long handle, boolean successful);
60 void onEscrowTokenActiveStateChanged(long handle, boolean active); 60 void onEscrowTokenActiveStateChanged(long handle, boolean active);
61
62 /** Management */
63 int getUserIdByEscrowTokenHandle(long tokenHandle);
61} 64}
diff --git a/car-lib/src/android/car/user/CarUserManagerHelper.java b/car-lib/src/android/car/user/CarUserManagerHelper.java
index 4a69f0b7..ce509436 100644
--- a/car-lib/src/android/car/user/CarUserManagerHelper.java
+++ b/car-lib/src/android/car/user/CarUserManagerHelper.java
@@ -15,7 +15,9 @@
15 */ 15 */
16package android.car.user; 16package android.car.user;
17 17
18import android.Manifest;
18import android.annotation.Nullable; 19import android.annotation.Nullable;
20import android.annotation.RequiresPermission;
19import android.app.ActivityManager; 21import android.app.ActivityManager;
20import android.car.settings.CarSettings; 22import android.car.settings.CarSettings;
21import android.content.BroadcastReceiver; 23import android.content.BroadcastReceiver;
@@ -26,15 +28,21 @@ import android.content.pm.UserInfo;
26import android.graphics.Bitmap; 28import android.graphics.Bitmap;
27import android.graphics.drawable.BitmapDrawable; 29import android.graphics.drawable.BitmapDrawable;
28import android.graphics.drawable.Drawable; 30import android.graphics.drawable.Drawable;
31import android.os.Bundle;
29import android.os.SystemProperties; 32import android.os.SystemProperties;
30import android.os.UserHandle; 33import android.os.UserHandle;
31import android.os.UserManager; 34import android.os.UserManager;
35import android.provider.Settings;
32import android.util.Log; 36import android.util.Log;
33 37
34import com.android.internal.util.UserIcons; 38import com.android.internal.util.UserIcons;
35 39
40import com.google.android.collect.Sets;
41
42import java.util.ArrayList;
36import java.util.Iterator; 43import java.util.Iterator;
37import java.util.List; 44import java.util.List;
45import java.util.Set;
38 46
39/** 47/**
40 * Helper class for {@link UserManager}, this is meant to be used by builds that support 48 * Helper class for {@link UserManager}, this is meant to be used by builds that support
@@ -49,19 +57,47 @@ import java.util.List;
49public class CarUserManagerHelper { 57public class CarUserManagerHelper {
50 private static final String TAG = "CarUserManagerHelper"; 58 private static final String TAG = "CarUserManagerHelper";
51 private static final String HEADLESS_SYSTEM_USER = "android.car.systemuser.headless"; 59 private static final String HEADLESS_SYSTEM_USER = "android.car.systemuser.headless";
60 /**
61 * Default set of restrictions for Non-Admin users.
62 */
63 private static final Set<String> DEFAULT_NON_ADMIN_RESTRICTIONS = Sets.newArraySet(
64 UserManager.DISALLOW_FACTORY_RESET
65 );
66 /**
67 * Default set of restrictions for Guest users.
68 */
69 private static final Set<String> DEFAULT_GUEST_RESTRICTIONS = Sets.newArraySet(
70 UserManager.DISALLOW_FACTORY_RESET,
71 UserManager.DISALLOW_REMOVE_USER,
72 UserManager.DISALLOW_MODIFY_ACCOUNTS,
73 UserManager.DISALLOW_OUTGOING_CALLS,
74 UserManager.DISALLOW_SMS,
75 UserManager.DISALLOW_INSTALL_APPS,
76 UserManager.DISALLOW_UNINSTALL_APPS
77 );
78
52 private final Context mContext; 79 private final Context mContext;
53 private final UserManager mUserManager; 80 private final UserManager mUserManager;
54 private final ActivityManager mActivityManager; 81 private final ActivityManager mActivityManager;
82 private int mLastActiveUser = UserHandle.USER_SYSTEM;
55 private Bitmap mDefaultGuestUserIcon; 83 private Bitmap mDefaultGuestUserIcon;
56 private OnUsersUpdateListener mUpdateListener; 84 private ArrayList<OnUsersUpdateListener> mUpdateListeners;
57 private final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { 85 private final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
58 @Override 86 @Override
59 public void onReceive(Context context, Intent intent) { 87 public void onReceive(Context context, Intent intent) {
60 mUpdateListener.onUsersUpdate(); 88 ArrayList<OnUsersUpdateListener> copyOfUpdateListeners;
89 synchronized (mUpdateListeners) {
90 copyOfUpdateListeners = new ArrayList(mUpdateListeners);
91 }
92
93 for (OnUsersUpdateListener listener : copyOfUpdateListeners) {
94 listener.onUsersUpdate();
95 }
61 } 96 }
62 }; 97 };
63 98
64 public CarUserManagerHelper(Context context) { 99 public CarUserManagerHelper(Context context) {
100 mUpdateListeners = new ArrayList<>();
65 mContext = context.getApplicationContext(); 101 mContext = context.getApplicationContext();
66 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 102 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
67 mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 103 mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -70,24 +106,139 @@ public class CarUserManagerHelper {
70 /** 106 /**
71 * Registers a listener for updates to all users - removing, adding users or changing user info. 107 * Registers a listener for updates to all users - removing, adding users or changing user info.
72 * 108 *
73 * <p> Best practise is to keep one listener per helper.
74 *
75 * @param listener Instance of {@link OnUsersUpdateListener}. 109 * @param listener Instance of {@link OnUsersUpdateListener}.
76 */ 110 */
77 public void registerOnUsersUpdateListener(OnUsersUpdateListener listener) { 111 public void registerOnUsersUpdateListener(OnUsersUpdateListener listener) {
78 if (mUpdateListener != null) { 112 if (listener == null) {
79 unregisterOnUsersUpdateListener(); 113 return;
114 }
115
116 synchronized (mUpdateListeners) {
117 if (mUpdateListeners.isEmpty()) {
118 // First listener being added, register receiver.
119 registerReceiver();
120 }
121
122 if (!mUpdateListeners.contains(listener)) {
123 mUpdateListeners.add(listener);
124 }
125 }
126 }
127
128 /**
129 * Unregisters on user update listener.
130 * Unregisters {@code BroadcastReceiver} if no listeners remain.
131 *
132 * @param listener Instance of {@link OnUsersUpdateListener} to unregister.
133 */
134 public void unregisterOnUsersUpdateListener(OnUsersUpdateListener listener) {
135 synchronized (mUpdateListeners) {
136 if (mUpdateListeners.contains(listener)) {
137 mUpdateListeners.remove(listener);
138
139 if (mUpdateListeners.isEmpty()) {
140 // No more listeners, unregister broadcast receiver.
141 unregisterReceiver();
142 }
143 }
80 } 144 }
145 }
81 146
82 mUpdateListener = listener; 147 /**
83 registerReceiver(); 148 * Set default boot into user.
149 *
150 * @param userId default user id to boot into.
151 */
152 public void setDefaultBootUser(int userId) {
153 Settings.Global.putInt(
154 mContext.getContentResolver(),
155 CarSettings.Global.DEFAULT_USER_ID_TO_BOOT_INTO, userId);
156 }
157
158 /**
159 * Set last active user.
160 *
161 * @param userId last active user id.
162 * @param skipGlobalSetting whether to skip set the global settings value.
163 */
164 public void setLastActiveUser(int userId, boolean skipGlobalSetting) {
165 mLastActiveUser = userId;
166 if (!skipGlobalSetting) {
167 Settings.Global.putInt(
168 mContext.getContentResolver(), CarSettings.Global.LAST_ACTIVE_USER_ID, userId);
169 }
84 } 170 }
85 171
86 /** 172 /**
87 * Unregisters on user update listener by unregistering {@code BroadcastReceiver}. 173 * Get user id for the default boot into user.
174 *
175 * @return user id of the default boot into user
176 */
177 public int getDefaultBootUser() {
178 // Make user 10 the original default boot user.
179 return Settings.Global.getInt(
180 mContext.getContentResolver(), CarSettings.Global.DEFAULT_USER_ID_TO_BOOT_INTO,
181 /* default user id= */ 10);
182 }
183
184 /**
185 * Get user id for the last active user.
186 *
187 * @return user id of the last active user.
88 */ 188 */
89 public void unregisterOnUsersUpdateListener() { 189 public int getLastActiveUser() {
90 unregisterReceiver(); 190 if (mLastActiveUser != UserHandle.USER_SYSTEM) {
191 return mLastActiveUser;
192 }
193 return Settings.Global.getInt(
194 mContext.getContentResolver(), CarSettings.Global.LAST_ACTIVE_USER_ID,
195 /* default user id= */ UserHandle.USER_SYSTEM);
196 }
197
198 /**
199 * Get user id for the initial user to boot into. This is only applicable for headless
200 * system user model.
201 *
202 * <p>If failed to retrieve the id stored in global settings or the retrieved id does not
203 * exist on device, then return the user with smallest user id.
204 *
205 * @return user id of the last active user or the smallest user id on the device.
206 */
207 public int getInitialUser() {
208 int lastActiveUserId = getLastActiveUser();
209
210 boolean isUserExist = false;
211 List<UserInfo> allUsers = getAllPersistentUsers();
212 int smallestUserId = Integer.MAX_VALUE;
213 for (UserInfo user : allUsers) {
214 if (user.id == lastActiveUserId) {
215 isUserExist = true;
216 }
217 smallestUserId = Math.min(user.id, smallestUserId);
218 }
219
220 // If the last active user is system user or the user id doesn't exist on device,
221 // return the smallest id or all users.
222 if (lastActiveUserId == UserHandle.USER_SYSTEM || !isUserExist) {
223 Log.e(TAG, "Can't get last active user id or the user no longer exist, user id: ."
224 + lastActiveUserId);
225 lastActiveUserId = smallestUserId;
226 }
227
228 return lastActiveUserId;
229 }
230
231 /**
232 * Sets default guest restrictions that will be applied every time a Guest user is created.
233 *
234 * <p> Restrictions are written to disk and persistent across boots.
235 */
236 public void initDefaultGuestRestrictions() {
237 Bundle defaultGuestRestrictions = new Bundle();
238 for (String restriction : DEFAULT_GUEST_RESTRICTIONS) {
239 defaultGuestRestrictions.putBoolean(restriction, true);
240 }
241 mUserManager.setDefaultGuestRestrictions(defaultGuestRestrictions);
91 } 242 }
92 243
93 /** 244 /**
@@ -151,8 +302,9 @@ public class CarUserManagerHelper {
151 } 302 }
152 303
153 /** 304 /**
154 * Gets all the existing foreground users on the system that are not currently running as 305 * Gets all the existing users on the system that are not currently running as
155 * the foreground user. 306 * the foreground user.
307 * These are all the users that can be switched to from the foreground user.
156 * 308 *
157 * @return List of {@code UserInfo} for each user that is not the foreground user. 309 * @return List of {@code UserInfo} for each user that is not the foreground user.
158 */ 310 */
@@ -173,8 +325,61 @@ public class CarUserManagerHelper {
173 if (isHeadlessSystemUser()) { 325 if (isHeadlessSystemUser()) {
174 return getAllUsersExceptSystemUserAndSpecifiedUser(UserHandle.USER_SYSTEM); 326 return getAllUsersExceptSystemUserAndSpecifiedUser(UserHandle.USER_SYSTEM);
175 } else { 327 } else {
176 return mUserManager.getUsers(/* excludeDying= */true); 328 return mUserManager.getUsers(/* excludeDying= */ true);
329 }
330 }
331
332 /**
333 * Gets all the users that are non-ephemeral and can be brought to the foreground on the system.
334 *
335 * @return List of {@code UserInfo} for non-ephemeral users that associated with a real person.
336 */
337 public List<UserInfo> getAllPersistentUsers() {
338 List<UserInfo> users = getAllUsers();
339 for (Iterator<UserInfo> iterator = users.iterator(); iterator.hasNext(); ) {
340 UserInfo userInfo = iterator.next();
341 if (userInfo.isEphemeral()) {
342 // Remove user that is ephemeral.
343 iterator.remove();
344 }
345 }
346 return users;
347 }
348
349 /**
350 * Gets all the users that can be brought to the foreground on the system that have admin roles.
351 *
352 * @return List of {@code UserInfo} for admin users that associated with a real person.
353 */
354 public List<UserInfo> getAllAdminUsers() {
355 List<UserInfo> users = getAllUsers();
356
357 for (Iterator<UserInfo> iterator = users.iterator(); iterator.hasNext(); ) {
358 UserInfo userInfo = iterator.next();
359 if (!userInfo.isAdmin()) {
360 // Remove user that is not admin.
361 iterator.remove();
362 }
363 }
364 return users;
365 }
366
367 /**
368 * Gets all users that are not guests.
369 *
370 * @return List of {@code UserInfo} for all users who are not guest users.
371 */
372 public List<UserInfo> getAllUsersExceptGuests() {
373 List<UserInfo> users = getAllUsers();
374
375 for (Iterator<UserInfo> iterator = users.iterator(); iterator.hasNext(); ) {
376 UserInfo userInfo = iterator.next();
377 if (userInfo.isGuest()) {
378 // Remove guests.
379 iterator.remove();
380 }
177 } 381 }
382 return users;
178 } 383 }
179 384
180 /** 385 /**
@@ -215,6 +420,62 @@ public class CarUserManagerHelper {
215 return users; 420 return users;
216 } 421 }
217 422
423 /**
424 * Maximum number of users allowed on the device. This includes real users, managed profiles
425 * and restricted users, but excludes guests.
426 *
427 * <p> It excludes system user in headless system user model.
428 *
429 * @return Maximum number of users that can be present on the device.
430 */
431 public int getMaxSupportedUsers() {
432 if (isHeadlessSystemUser()) {
433 return UserManager.getMaxSupportedUsers() - 1;
434 }
435 return UserManager.getMaxSupportedUsers();
436 }
437
438 /**
439 * Get the maximum number of real (non-guest, non-managed profile) users that can be created on
440 * the device. This is a dynamic value and it decreases with the increase of the number of
441 * managed profiles on the device.
442 *
443 * <p> It excludes system user in headless system user model.
444 *
445 * @return Maximum number of real users that can be created.
446 */
447 public int getMaxSupportedRealUsers() {
448 return getMaxSupportedUsers() - getManagedProfilesCount();
449 }
450
451 /**
452 * Returns true if the maximum number of users on the device has been reached, false otherwise.
453 */
454 public boolean isUserLimitReached() {
455 int countNonGuestUsers = getAllUsersExceptGuests().size();
456 int maxSupportedUsers = getMaxSupportedUsers();
457
458 if (countNonGuestUsers > maxSupportedUsers) {
459 Log.e(TAG, "There are more users on the device than allowed.");
460 return true;
461 }
462
463 return getAllUsersExceptGuests().size() == maxSupportedUsers;
464 }
465
466 private int getManagedProfilesCount() {
467 List<UserInfo> users = getAllUsers();
468
469 // Count all users that are managed profiles of another user.
470 int managedProfilesCount = 0;
471 for (UserInfo user : users) {
472 if (user.isManagedProfile()) {
473 managedProfilesCount++;
474 }
475 }
476 return managedProfilesCount;
477 }
478
218 // User information accessors 479 // User information accessors
219 480
220 /** 481 /**
@@ -234,7 +495,17 @@ public class CarUserManagerHelper {
234 * @return {@code true} if is default user, {@code false} otherwise. 495 * @return {@code true} if is default user, {@code false} otherwise.
235 */ 496 */
236 public boolean isDefaultUser(UserInfo userInfo) { 497 public boolean isDefaultUser(UserInfo userInfo) {
237 return userInfo.id == CarSettings.DEFAULT_USER_ID_TO_BOOT_INTO; 498 return userInfo.id == getDefaultBootUser();
499 }
500
501 /**
502 * Checks whether the user is last active user.
503 *
504 * @param userInfo User to check against last active user.
505 * @return {@code true} if is last active user, {@code false} otherwise.
506 */
507 public boolean isLastActiveUser(UserInfo userInfo) {
508 return userInfo.id == getLastActiveUser();
238 } 509 }
239 510
240 /** 511 /**
@@ -267,6 +538,24 @@ public class CarUserManagerHelper {
267 } 538 }
268 539
269 /** 540 /**
541 * Checks if the foreground user is ephemeral.
542 */
543 public boolean isForegroundUserEphemeral() {
544 return getCurrentForegroundUserInfo().isEphemeral();
545 }
546
547 /**
548 * Checks if the given user is non-ephemeral.
549 *
550 * @param userId User to check
551 * @return {@code true} if given user is persistent user.
552 */
553 public boolean isPersistentUser(int userId) {
554 UserInfo user = mUserManager.getUserInfo(userId);
555 return !user.isEphemeral();
556 }
557
558 /**
270 * Returns whether this user can be removed from the system. 559 * Returns whether this user can be removed from the system.
271 * 560 *
272 * @param userInfo User to be removed 561 * @param userInfo User to be removed
@@ -311,6 +600,13 @@ public class CarUserManagerHelper {
311 } 600 }
312 601
313 /** 602 /**
603 * Checks if the calling app is running as an admin user.
604 */
605 public boolean isCurrentProcessAdminUser() {
606 return mUserManager.isAdminUser();
607 }
608
609 /**
314 * Checks if the calling app is running as a guest user. 610 * Checks if the calling app is running as a guest user.
315 */ 611 */
316 public boolean isCurrentProcessGuestUser() { 612 public boolean isCurrentProcessGuestUser() {
@@ -369,17 +665,44 @@ public class CarUserManagerHelper {
369 } 665 }
370 666
371 /** 667 /**
668 * Assigns admin privileges to the user.
669 *
670 * @param user User to be upgraded to Admin status.
671 */
672 @RequiresPermission(allOf = {
673 Manifest.permission.INTERACT_ACROSS_USERS_FULL,
674 Manifest.permission.MANAGE_USERS
675 })
676 public void assignAdminPrivileges(UserInfo user) {
677 if (!isCurrentProcessAdminUser()) {
678 Log.w(TAG, "Only admin users can assign admin privileges.");
679 return;
680 }
681
682 mUserManager.setUserAdmin(user.id);
683
684 // Remove restrictions imposed on non-admins.
685 setDefaultNonAdminRestrictions(user, /* enable= */ false);
686 }
687
688 /**
372 * Creates a new user on the system, the created user would be granted admin role. 689 * Creates a new user on the system, the created user would be granted admin role.
690 * Only admins can create other admins.
373 * 691 *
374 * @param userName Name to give to the newly created user. 692 * @param userName Name to give to the newly created user.
375 * @return Newly created admin user, null if failed to create a user. 693 * @return Newly created admin user, null if failed to create a user.
376 */ 694 */
377 @Nullable 695 @Nullable
378 public UserInfo createNewAdminUser(String userName) { 696 public UserInfo createNewAdminUser(String userName) {
697 if (!(isCurrentProcessAdminUser() || isCurrentProcessSystemUser())) {
698 // Only Admins or System user can create other privileged users.
699 Log.e(TAG, "Only admin users and system user can create other admins.");
700 return null;
701 }
702
379 UserInfo user = mUserManager.createUser(userName, UserInfo.FLAG_ADMIN); 703 UserInfo user = mUserManager.createUser(userName, UserInfo.FLAG_ADMIN);
380 if (user == null) { 704 if (user == null) {
381 // Couldn't create user, most likely because there are too many, but we haven't 705 // Couldn't create user, most likely because there are too many.
382 // been able to reload the list yet.
383 Log.w(TAG, "can't create admin user."); 706 Log.w(TAG, "can't create admin user.");
384 return null; 707 return null;
385 } 708 }
@@ -397,16 +720,47 @@ public class CarUserManagerHelper {
397 public UserInfo createNewNonAdminUser(String userName) { 720 public UserInfo createNewNonAdminUser(String userName) {
398 UserInfo user = mUserManager.createUser(userName, 0); 721 UserInfo user = mUserManager.createUser(userName, 0);
399 if (user == null) { 722 if (user == null) {
400 // Couldn't create user, most likely because there are too many, but we haven't 723 // Couldn't create user, most likely because there are too many.
401 // been able to reload the list yet.
402 Log.w(TAG, "can't create non-admin user."); 724 Log.w(TAG, "can't create non-admin user.");
403 return null; 725 return null;
404 } 726 }
727 setDefaultNonAdminRestrictions(user, /* enable= */ true);
728
729 // Each non-admin has sms and outgoing call restrictions applied by the UserManager on
730 // creation. We want to enable these permissions by default in the car.
731 setUserRestriction(user, UserManager.DISALLOW_SMS, /* enable= */ false);
732 setUserRestriction(user, UserManager.DISALLOW_OUTGOING_CALLS, /* enable= */ false);
733
405 assignDefaultIcon(user); 734 assignDefaultIcon(user);
406 return user; 735 return user;
407 } 736 }
408 737
409 /** 738 /**
739 * Sets the values of default Non-Admin restrictions to the passed in value.
740 *
741 * @param userInfo User to set restrictions on.
742 * @param enable If true, restriction is ON, If false, restriction is OFF.
743 */
744 private void setDefaultNonAdminRestrictions(UserInfo userInfo, boolean enable) {
745 for (String restriction : DEFAULT_NON_ADMIN_RESTRICTIONS) {
746 setUserRestriction(userInfo, restriction, enable);
747 }
748 }
749
750 /**
751 * Sets the value of the specified restriction for the specified user.
752 *
753 * @param userInfo the user whose restriction is to be changed
754 * @param restriction the key of the restriction
755 * @param enable the value for the restriction. if true, turns the restriction ON, if false,
756 * turns the restriction OFF.
757 */
758 public void setUserRestriction(UserInfo userInfo, String restriction, boolean enable) {
759 UserHandle userHandle = UserHandle.of(userInfo.id);
760 mUserManager.setUserRestriction(restriction, enable, userHandle);
761 }
762
763 /**
410 * Tries to remove the user that's passed in. System user cannot be removed. 764 * Tries to remove the user that's passed in. System user cannot be removed.
411 * If the user to be removed is user currently running the process, 765 * If the user to be removed is user currently running the process,
412 * it switches to the guest user first, and then removes the user. 766 * it switches to the guest user first, and then removes the user.
@@ -420,10 +774,15 @@ public class CarUserManagerHelper {
420 return false; 774 return false;
421 } 775 }
422 776
423 // Not allow to delete the default user for now. Since default user is the one to 777 // Not allow to delete the last admin user on the device for now.
424 // boot into. 778 if (userInfo.isAdmin() && getAllAdminUsers().size() <= 1) {
425 if (isHeadlessSystemUser() && isDefaultUser(userInfo)) { 779 Log.w(TAG, "User " + userInfo.id + " is the last admin user on device.");
426 Log.w(TAG, "User " + userInfo.id + " is the default user, could not be removed."); 780 return false;
781 }
782
783 if (!isCurrentProcessAdminUser() && !isCurrentProcessUser(userInfo)) {
784 // If the caller is non-admin, they can only delete themselves.
785 Log.e(TAG, "Non-admins cannot remove other users.");
427 return false; 786 return false;
428 } 787 }
429 788
diff --git a/car-support-lib/proguard-release.flags b/car-support-lib/proguard-release.flags
index 91cab7ea..4f5f9178 100644
--- a/car-support-lib/proguard-release.flags
+++ b/car-support-lib/proguard-release.flags
@@ -7201,6 +7201,7 @@
7201 public int enabledSetting; 7201 public int enabledSetting;
7202 public int flags; 7202 public int flags;
7203 public int fullBackupContent; 7203 public int fullBackupContent;
7204 public boolean hiddenUntilInstalled;
7204 public int installLocation; 7205 public int installLocation;
7205 public int largestWidthLimitDp; 7206 public int largestWidthLimitDp;
7206 public long longVersionCode; 7207 public long longVersionCode;
@@ -7646,6 +7647,8 @@
7646 public abstract java.lang.String[] setPackagesSuspendedAsUser(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String, java.lang.String, int); 7647 public abstract java.lang.String[] setPackagesSuspendedAsUser(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String, java.lang.String, int);
7647 public abstract void setPermissionEnforced(java.lang.String, boolean); 7648 public abstract void setPermissionEnforced(java.lang.String, boolean);
7648 public abstract boolean setRequiredForSystemUser(java.lang.String, boolean); 7649 public abstract boolean setRequiredForSystemUser(java.lang.String, boolean);
7650 public abstract void setSystemAppHiddenUntilInstalled(java.lang.String, boolean);
7651 public abstract boolean setSystemAppInstallState(java.lang.String, boolean, int);
7649 public abstract void setUpdateAvailable(java.lang.String, boolean); 7652 public abstract void setUpdateAvailable(java.lang.String, boolean);
7650 public abstract boolean shouldShowRequestPermissionRationale(java.lang.String, java.lang.String, int); 7653 public abstract boolean shouldShowRequestPermissionRationale(java.lang.String, java.lang.String, int);
7651 public abstract void systemReady(); 7654 public abstract void systemReady();
@@ -8589,6 +8592,7 @@
8589 public static int MATCH_DISABLED_UNTIL_USED_COMPONENTS; 8592 public static int MATCH_DISABLED_UNTIL_USED_COMPONENTS;
8590 public static int MATCH_EXPLICITLY_VISIBLE_ONLY; 8593 public static int MATCH_EXPLICITLY_VISIBLE_ONLY;
8591 public static int MATCH_FACTORY_ONLY; 8594 public static int MATCH_FACTORY_ONLY;
8595 public static int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
8592 public static int MATCH_INSTANT; 8596 public static int MATCH_INSTANT;
8593 public static int MATCH_KNOWN_PACKAGES; 8597 public static int MATCH_KNOWN_PACKAGES;
8594 public static int MATCH_STATIC_SHARED_LIBRARIES; 8598 public static int MATCH_STATIC_SHARED_LIBRARIES;
@@ -17003,8 +17007,8 @@
17003 17007
17004 public boolean equals(java.lang.Object); 17008 public boolean equals(java.lang.Object);
17005 public static android.view.DisplayCutout fromBoundingRect(int, int, int, int); 17009 public static android.view.DisplayCutout fromBoundingRect(int, int, int, int);
17006 public static android.view.DisplayCutout fromBounds(android.graphics.Path); 17010 public static android.view.DisplayCutout fromBounds(android.graphics.Region);
17007 public static android.view.DisplayCutout fromResources(android.content.res.Resources, int, int); 17011 public static android.view.DisplayCutout fromResourcesRectApproximation(android.content.res.Resources, int, int);
17008 public static android.view.DisplayCutout fromSpec(java.lang.String, int, int, float); 17012 public static android.view.DisplayCutout fromSpec(java.lang.String, int, int, float);
17009 public java.util.List getBoundingRects(); 17013 public java.util.List getBoundingRects();
17010 public android.graphics.Region getBounds(); 17014 public android.graphics.Region getBounds();
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index 8e55f8cd..52fcf2b5 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -24,6 +24,8 @@
24 <bool name="config_enableMultiUserUI">true</bool> 24 <bool name="config_enableMultiUserUI">true</bool>
25 <!-- Arbitrary max 8 users. --> 25 <!-- Arbitrary max 8 users. -->
26 <integer name="config_multiuserMaximumUsers">8</integer> 26 <integer name="config_multiuserMaximumUsers">8</integer>
27 <!-- If true, all guest users created on the device will be ephemeral. -->
28 <bool name="config_guestUserEphemeral">true</bool>
27 <!-- Car Mode --> 29 <!-- Car Mode -->
28 <integer name="config_defaultUiModeType">3</integer> 30 <integer name="config_defaultUiModeType">3</integer>
29 <!-- Can't leave car mode --> 31 <!-- Can't leave car mode -->
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
new file mode 100644
index 00000000..bd94b206
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
@@ -0,0 +1,85 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3**
4** Copyright 2018, The Android Open Source Project
5**
6** Licensed under the Apache License, Version 2.0 (the "License")
7** you may not use this file except in compliance with the License.
8** You may obtain a copy of the License at
9**
10** http://www.apache.org/licenses/LICENSE-2.0
11**
12** Unless required by applicable law or agreed to in writing, software
13** distributed under the License is distributed on an "AS IS" BASIS,
14** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15** See the License for the specific language governing permissions and
16** limitations under the License.
17*/
18-->
19
20<!-- Car customizations
21 - Added title "Enter your Pattern" at the top
22 - Hid the emergency call at the bottom
23-->
24
25<com.android.keyguard.KeyguardPatternView
26 xmlns:android="http://schemas.android.com/apk/res/android"
27 android:id="@+id/keyguard_pattern_view"
28 android:orientation="horizontal"
29 android:layout_width="match_parent"
30 android:layout_height="match_parent"
31 android:paddingHorizontal="@dimen/car_margin">
32
33 <FrameLayout
34 android:layout_height="match_parent"
35 android:layout_width="0dp"
36 android:layout_weight="1">
37
38 <com.android.internal.widget.LockPatternView
39 android:id="@+id/lockPatternView"
40 android:layout_width="@dimen/keyguard_pattern_dimension"
41 android:layout_height="@dimen/keyguard_pattern_dimension"
42 android:layout_gravity="center"/>
43 </FrameLayout>
44
45 <LinearLayout
46 android:id="@+id/container"
47 android:layout_height="match_parent"
48 android:layout_width="0dp"
49 android:layout_weight="1"
50 android:orientation="vertical"
51 android:gravity="center_vertical">
52
53 <TextView
54 android:layout_width="match_parent"
55 android:layout_height="wrap_content"
56 android:layout_margin="@dimen/car_padding_2"
57 android:gravity="center"
58 android:textColor="@android:color/white"
59 android:textSize="@dimen/car_body1_size"
60 android:text="@string/keyguard_enter_your_pattern" />
61
62 <include layout="@layout/keyguard_message_area"
63 android:layout_width="match_parent"
64 android:layout_height="wrap_content"
65 android:layout_marginBottom="@dimen/car_padding_4"/>
66
67 <Button
68 android:id="@+id/cancel_button"
69 android:layout_width="wrap_content"
70 android:layout_height="wrap_content"
71 android:layout_gravity="center"
72 style="@style/KeyguardButton"
73 android:text="@string/cancel"/>
74
75 <include layout="@layout/keyguard_eca"
76 android:id="@+id/keyguard_selector_fade_container"
77 android:layout_width="match_parent"
78 android:layout_height="wrap_content"
79 android:orientation="vertical"
80 android:layout_gravity="bottom|center_horizontal"
81 android:gravity="center_horizontal"
82 android:visibility="gone" />
83 </LinearLayout>
84
85</com.android.keyguard.KeyguardPatternView>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pin_view.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
index 0bca46be..788ecfc1 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
@@ -23,23 +23,25 @@
23--> 23-->
24 24
25<com.android.keyguard.KeyguardPINView 25<com.android.keyguard.KeyguardPINView
26 xmlns:android="http://schemas.android.com/apk/res/android" 26 xmlns:android="http://schemas.android.com/apk/res/android"
27 xmlns:app="http://schemas.android.com/apk/res-auto" 27 xmlns:app="http://schemas.android.com/apk/res-auto"
28 android:id="@+id/keyguard_pin_view" 28 android:id="@+id/keyguard_pin_view"
29 android:layout_width="match_parent" 29 android:layout_width="match_parent"
30 android:layout_height="match_parent"> 30 android:layout_height="match_parent"
31 android:orientation="horizontal"
32 android:paddingHorizontal="@dimen/car_margin">
31 33
32 <FrameLayout 34 <FrameLayout
33 android:layout_width="match_parent" 35 android:layout_width="0dp"
34 android:layout_height="match_parent" 36 android:layout_weight="1"
35 android:layout_marginLeft="@dimen/num_pad_margin_left" 37 android:layout_height="match_parent">
36 android:layout_marginRight="@dimen/num_pad_margin_right">
37 38
38 <GridLayout 39 <GridLayout
39 android:id="@+id/container" 40 android:id="@+id/container"
40 android:layout_width="wrap_content" 41 android:layout_width="wrap_content"
41 android:layout_height="wrap_content" 42 android:layout_height="wrap_content"
42 android:layout_gravity="center_vertical|start" 43 android:layout_gravity="center"
44 android:gravity="center"
43 android:columnCount="3"> 45 android:columnCount="3">
44 46
45 <!-- Row 1 --> 47 <!-- Row 1 -->
@@ -106,51 +108,51 @@
106 android:background="@drawable/ripple_drawable" 108 android:background="@drawable/ripple_drawable"
107 android:contentDescription="@string/keyboardview_keycode_enter" /> 109 android:contentDescription="@string/keyboardview_keycode_enter" />
108 </GridLayout> 110 </GridLayout>
111 </FrameLayout>
109 112
110 <LinearLayout 113 <LinearLayout
114 android:layout_width="0dp"
115 android:layout_height="match_parent"
116 android:layout_weight="1"
117 android:gravity="center"
118 android:orientation="vertical">
119
120 <com.android.keyguard.PasswordTextView
121 android:id="@+id/pinEntry"
122 android:layout_width="@dimen/keyguard_security_width"
123 android:layout_height="@dimen/pin_entry_height"
124 android:gravity="center"
125 app:scaledTextSize="@integer/password_text_view_scale"
126 android:contentDescription="@string/keyguard_accessibility_pin_area" />
127
128 <View
129 android:id="@+id/divider"
130 android:layout_width="@dimen/keyguard_security_width"
131 android:layout_height="@dimen/divider_height"
132 android:background="@android:color/white" />
133
134 <TextView
111 android:layout_width="wrap_content" 135 android:layout_width="wrap_content"
112 android:layout_height="wrap_content" 136 android:layout_height="wrap_content"
113 android:layout_gravity="center_vertical|end" 137 android:layout_margin="@dimen/car_padding_2"
114 android:gravity="center" 138 android:gravity="center"
115 android:orientation="vertical"> 139 android:textColor="@android:color/white"
116 140 android:textSize="@dimen/car_body1_size"
117 <com.android.keyguard.PasswordTextView 141 android:text="@string/keyguard_enter_your_pin" />
118 android:id="@+id/pinEntry" 142
119 android:layout_width="@dimen/keyguard_security_width" 143 <include layout="@layout/keyguard_message_area"
120 android:layout_height="@dimen/pin_entry_height" 144 android:layout_width="wrap_content"
121 android:gravity="center" 145 android:layout_height="wrap_content"
122 app:scaledTextSize="@integer/password_text_view_scale" 146 android:layout_marginBottom="@dimen/car_padding_4"/>
123 android:contentDescription="@string/keyguard_accessibility_pin_area" /> 147
124 148 <Button
125 <View 149 android:id="@+id/cancel_button"
126 android:id="@+id/divider" 150 android:layout_width="wrap_content"
127 android:layout_width="@dimen/keyguard_security_width" 151 android:layout_height="wrap_content"
128 android:layout_height="@dimen/divider_height" 152 android:layout_gravity="center"
129 android:background="@android:color/white" /> 153 style="@style/KeyguardButton"
130 154 android:text="@string/cancel"/>
131 <TextView 155 </LinearLayout>
132 android:layout_width="wrap_content"
133 android:layout_height="wrap_content"
134 android:layout_margin="@dimen/car_padding_2"
135 android:gravity="center"
136 android:textColor="@android:color/white"
137 android:textSize="@dimen/car_body1_size"
138 android:text="@string/keyguard_enter_your_pin" />
139
140 <include layout="@layout/keyguard_message_area"
141 android:layout_width="wrap_content"
142 android:layout_height="wrap_content"
143 android:layout_marginBottom="@dimen/car_padding_4"/>
144
145 <Button
146 android:id="@+id/cancel_button"
147 android:layout_width="wrap_content"
148 android:layout_height="wrap_content"
149 android:layout_gravity="center"
150 style="@style/KeyguardButton"
151 android:text="@string/cancel"/>
152 </LinearLayout>
153 </FrameLayout>
154 156
155 <!-- KeyguardPinView references these resources ids in code so removing them will cause the 157 <!-- KeyguardPinView references these resources ids in code so removing them will cause the
156 keyguard to crash. Instead put them down here where they are out of the way and set their 158 keyguard to crash. Instead put them down here where they are out of the way and set their
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml
index ea9f5bd1..b7967c4a 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -23,7 +23,7 @@
23 <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen> 23 <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
24 <dimen name="divider_height">1dp</dimen> 24 <dimen name="divider_height">1dp</dimen>
25 <dimen name="key_enter_margin_top">128dp</dimen> 25 <dimen name="key_enter_margin_top">128dp</dimen>
26 <dimen name="keyguard_pattern_dimension">350dp</dimen> 26 <dimen name="keyguard_pattern_dimension">400dp</dimen>
27 <dimen name="password_field_width">350dp</dimen> 27 <dimen name="password_field_width">350dp</dimen>
28 <dimen name="pin_pattern_pad_margin_vertical">0dp</dimen> 28 <dimen name="pin_pattern_pad_margin_vertical">0dp</dimen>
29</resources> 29</resources>
diff --git a/car_product/sepolicy/private/carservice_app.te b/car_product/sepolicy/private/carservice_app.te
index d02c3777..fa2577ff 100644
--- a/car_product/sepolicy/private/carservice_app.te
+++ b/car_product/sepolicy/private/carservice_app.te
@@ -28,12 +28,14 @@ allow carservice_app {
28 input_method_service 28 input_method_service
29 input_service 29 input_service
30 location_service 30 location_service
31 media_session_service
31 network_management_service 32 network_management_service
32 power_service 33 power_service
33 procfsinspector_service 34 procfsinspector_service
34 sensorservice_service 35 sensorservice_service
35 surfaceflinger_service 36 surfaceflinger_service
36 uimode_service 37 uimode_service
38 voiceinteraction_service
37}:service_manager find; 39}:service_manager find;
38 40
39# Read and write /data/data subdirectory. 41# Read and write /data/data subdirectory.
diff --git a/evs/app/RenderDirectView.cpp b/evs/app/RenderDirectView.cpp
index 24eb4854..f0d26e40 100644
--- a/evs/app/RenderDirectView.cpp
+++ b/evs/app/RenderDirectView.cpp
@@ -133,6 +133,9 @@ bool RenderDirectView::drawFrame(const BufferDesc& tgtBuffer) {
133 glDisableVertexAttribArray(1); 133 glDisableVertexAttribArray(1);
134 134
135 135
136 // Now that everything is submitted, release our hold on the texture resource
137 detachRenderTarget();
138
136 // Wait for the rendering to finish 139 // Wait for the rendering to finish
137 glFinish(); 140 glFinish();
138 141
diff --git a/evs/app/RenderTopView.cpp b/evs/app/RenderTopView.cpp
index bff2b3c0..80ccb116 100644
--- a/evs/app/RenderTopView.cpp
+++ b/evs/app/RenderTopView.cpp
@@ -212,6 +212,9 @@ bool RenderTopView::drawFrame(const BufferDesc& tgtBuffer) {
212 // Draw the car image 212 // Draw the car image
213 renderCarTopView(); 213 renderCarTopView();
214 214
215 // Now that everythign is submitted, release our hold on the texture resource
216 detachRenderTarget();
217
215 // Wait for the rendering to finish 218 // Wait for the rendering to finish
216 glFinish(); 219 glFinish();
217 220
diff --git a/evs/sampleDriver/GlWrapper.cpp b/evs/sampleDriver/GlWrapper.cpp
index 3055ba0f..fbd36a24 100644
--- a/evs/sampleDriver/GlWrapper.cpp
+++ b/evs/sampleDriver/GlWrapper.cpp
@@ -308,6 +308,11 @@ bool GlWrapper::initialize() {
308 return false; 308 return false;
309 } 309 }
310 310
311 // Turn off mip-mapping for the created texture surface
312 // (the inbound camera imagery doesn't have MIPs)
313 glBindTexture(GL_TEXTURE_2D, mTextureMap);
314 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
315 glBindTexture(GL_TEXTURE_2D, 0);
311 316
312 return true; 317 return true;
313} 318}
diff --git a/evs/sampleDriver/ServiceNames.h b/evs/sampleDriver/ServiceNames.h
index 1178da5a..6458b1b7 100644
--- a/evs/sampleDriver/ServiceNames.h
+++ b/evs/sampleDriver/ServiceNames.h
@@ -13,5 +13,9 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_SERVICENAMES_H
17#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_SERVICENAMES_H
16 18
17const static char kEnumeratorServiceName[] = "EvsEnumeratorHw"; 19const static char kEnumeratorServiceName[] = "EvsEnumeratorHw";
20
21#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_SERVICENAMES_H
diff --git a/evs/sampleDriver/VideoCapture.h b/evs/sampleDriver/VideoCapture.h
index f2d11752..63305b91 100644
--- a/evs/sampleDriver/VideoCapture.h
+++ b/evs/sampleDriver/VideoCapture.h
@@ -13,6 +13,9 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_VIDEOCAPTURE_H
17#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_VIDEOCAPTURE_H
18
16#include <atomic> 19#include <atomic>
17#include <thread> 20#include <thread>
18#include <functional> 21#include <functional>
@@ -73,3 +76,4 @@ private:
73 }; 76 };
74}; 77};
75 78
79#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_VIDEOCAPTURE_
diff --git a/service/Android.mk b/service/Android.mk
index c03042d6..d9ae9ecc 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -27,6 +27,8 @@ include $(CLEAR_VARS)
27 27
28LOCAL_SRC_FILES := $(car_service_sources) 28LOCAL_SRC_FILES := $(car_service_sources)
29 29
30LOCAL_USE_AAPT2 := true
31
30LOCAL_PACKAGE_NAME := CarService 32LOCAL_PACKAGE_NAME := CarService
31LOCAL_PRIVATE_PLATFORM_APIS := true 33LOCAL_PRIVATE_PLATFORM_APIS := true
32 34
@@ -47,6 +49,8 @@ LOCAL_STATIC_JAVA_LIBRARIES += \
47 car-systemtest \ 49 car-systemtest \
48 com.android.car.procfsinspector-client \ 50 com.android.car.procfsinspector-client \
49 51
52include frameworks/base/packages/SettingsLib/common.mk
53
50include $(BUILD_PACKAGE) 54include $(BUILD_PACKAGE)
51 55
52##################################################################################### 56#####################################################################################
@@ -72,6 +76,8 @@ LOCAL_STATIC_JAVA_LIBRARIES += \
72 car-systemtest \ 76 car-systemtest \
73 com.android.car.procfsinspector-client \ 77 com.android.car.procfsinspector-client \
74 78
79include frameworks/base/packages/SettingsLib/common.mk
80
75include $(BUILD_STATIC_JAVA_LIBRARY) 81include $(BUILD_STATIC_JAVA_LIBRARY)
76 82
77include $(call all-makefiles-under,$(LOCAL_PATH)) 83include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 5212448c..870ed100 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -27,11 +27,6 @@
27 android:description="@string/car_permission_desc" 27 android:description="@string/car_permission_desc"
28 android:label="@string/car_permission_label" /> 28 android:label="@string/car_permission_label" />
29 <permission 29 <permission
30 android:name="android.car.permission.ADJUST_CAR_CABIN"
31 android:protectionLevel="system|signature"
32 android:label="@string/car_permission_label_cabin"
33 android:description="@string/car_permission_desc_cabin" />
34 <permission
35 android:name="android.car.permission.CAR_ENERGY" 30 android:name="android.car.permission.CAR_ENERGY"
36 android:permissionGroup="android.car.permission-group.CAR_MONITORING" 31 android:permissionGroup="android.car.permission-group.CAR_MONITORING"
37 android:protectionLevel="dangerous" 32 android:protectionLevel="dangerous"
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 45e6e0b8..214cf026 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -24,6 +24,9 @@
24 dynamic audio routing is disabled and audio works in legacy mode. It may be useful 24 dynamic audio routing is disabled and audio works in legacy mode. It may be useful
25 during initial development where audio hal does not support bus based addressing yet. --> 25 during initial development where audio hal does not support bus based addressing yet. -->
26 <bool name="audioUseDynamicRouting">false</bool> 26 <bool name="audioUseDynamicRouting">false</bool>
27 <!-- Configuration to persist master mute state. If this is set to true,
28 Android will restore the master mute state on boot. -->
29 <bool name="audioPersistMasterMuteState">true</bool>
27 <!-- Whether to block other audio while media audio is muted with display off. When set to true, 30 <!-- Whether to block other audio while media audio is muted with display off. When set to true,
28 other sounds cannot be played either while display is off. If false, only media is muted 31 other sounds cannot be played either while display is off. If false, only media is muted
29 and other sounds can be still played. --> 32 and other sounds can be still played. -->
@@ -51,6 +54,9 @@
51 Format of each entry is either to specify package name to whitelist the whole package 54 Format of each entry is either to specify package name to whitelist the whole package
52 or use format of "packagename/activity_classname" for tagging each activities.--> 55 or use format of "packagename/activity_classname" for tagging each activities.-->
53 <string name="activityBlacklist"></string> 56 <string name="activityBlacklist"></string>
57 <!-- List of play store package names that are allowed sources of app installation-->
58 <string-array translateble="false" name="allowedAppInstallSources">
59 </string-array>
54 <!-- Default home activity --> 60 <!-- Default home activity -->
55 <string name="defaultHomeActivity"><!--com.your.package/com.your.package.Activity--></string> 61 <string name="defaultHomeActivity"><!--com.your.package/com.your.package.Activity--></string>
56 <!-- The com.android.car.VmsPublisherService will bind to this list of clients --> 62 <!-- The com.android.car.VmsPublisherService will bind to this list of clients -->
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index 1b7ee67d..1f9fad0e 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -18,6 +18,7 @@ package com.android.car;
18import android.annotation.NonNull; 18import android.annotation.NonNull;
19import android.annotation.Nullable; 19import android.annotation.Nullable;
20import android.car.Car; 20import android.car.Car;
21import android.car.media.CarAudioManager;
21import android.car.media.CarAudioPatchHandle; 22import android.car.media.CarAudioPatchHandle;
22import android.car.media.ICarAudio; 23import android.car.media.ICarAudio;
23import android.car.media.ICarVolumeCallback; 24import android.car.media.ICarVolumeCallback;
@@ -45,11 +46,13 @@ import android.media.audiopolicy.AudioPolicy;
45import android.os.IBinder; 46import android.os.IBinder;
46import android.os.Looper; 47import android.os.Looper;
47import android.os.RemoteException; 48import android.os.RemoteException;
49import android.provider.Settings;
48import android.telephony.TelephonyManager; 50import android.telephony.TelephonyManager;
49import android.text.TextUtils; 51import android.text.TextUtils;
50import android.util.Log; 52import android.util.Log;
51import android.util.SparseArray; 53import android.util.SparseArray;
52import android.util.SparseIntArray; 54import android.util.SparseIntArray;
55import android.view.KeyEvent;
53 56
54import com.android.internal.util.Preconditions; 57import com.android.internal.util.Preconditions;
55 58
@@ -125,6 +128,7 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
125 private final TelephonyManager mTelephonyManager; 128 private final TelephonyManager mTelephonyManager;
126 private final AudioManager mAudioManager; 129 private final AudioManager mAudioManager;
127 private final boolean mUseDynamicRouting; 130 private final boolean mUseDynamicRouting;
131 private final boolean mPersistMasterMuteState;
128 private final SparseIntArray mContextToBus = new SparseIntArray(); 132 private final SparseIntArray mContextToBus = new SparseIntArray();
129 private final SparseArray<CarAudioDeviceInfo> mCarAudioDeviceInfos = new SparseArray<>(); 133 private final SparseArray<CarAudioDeviceInfo> mCarAudioDeviceInfos = new SparseArray<>();
130 134
@@ -151,15 +155,15 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
151 } 155 }
152 break; 156 break;
153 case AudioManager.ADJUST_MUTE: 157 case AudioManager.ADJUST_MUTE:
154 mAudioManager.setMasterMute(true, flags); 158 setMasterMute(true, flags);
155 callbackMasterMuteChange(flags); 159 callbackMasterMuteChange(flags);
156 break; 160 break;
157 case AudioManager.ADJUST_UNMUTE: 161 case AudioManager.ADJUST_UNMUTE:
158 mAudioManager.setMasterMute(false, flags); 162 setMasterMute(false, flags);
159 callbackMasterMuteChange(flags); 163 callbackMasterMuteChange(flags);
160 break; 164 break;
161 case AudioManager.ADJUST_TOGGLE_MUTE: 165 case AudioManager.ADJUST_TOGGLE_MUTE:
162 mAudioManager.setMasterMute(!mAudioManager.isMasterMute(), flags); 166 setMasterMute(!mAudioManager.isMasterMute(), flags);
163 callbackMasterMuteChange(flags); 167 callbackMasterMuteChange(flags);
164 break; 168 break;
165 case AudioManager.ADJUST_SAME: 169 case AudioManager.ADJUST_SAME:
@@ -203,6 +207,8 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
203 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 207 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
204 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 208 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
205 mUseDynamicRouting = mContext.getResources().getBoolean(R.bool.audioUseDynamicRouting); 209 mUseDynamicRouting = mContext.getResources().getBoolean(R.bool.audioUseDynamicRouting);
210 mPersistMasterMuteState = mContext.getResources().getBoolean(
211 R.bool.audioPersistMasterMuteState);
206 } 212 }
207 213
208 /** 214 /**
@@ -219,6 +225,13 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
219 setupDynamicRouting(); 225 setupDynamicRouting();
220 setupVolumeGroups(); 226 setupVolumeGroups();
221 } 227 }
228
229 // Restore master mute state if applicable
230 if (mPersistMasterMuteState) {
231 boolean storedMasterMute = Settings.Global.getInt(mContext.getContentResolver(),
232 CarAudioManager.VOLUME_SETTINGS_KEY_MASTER_MUTE, 0) != 0;
233 setMasterMute(storedMasterMute, 0);
234 }
222 } 235 }
223 } 236 }
224 237
@@ -242,7 +255,8 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
242 public void dump(PrintWriter writer) { 255 public void dump(PrintWriter writer) {
243 writer.println("*CarAudioService*"); 256 writer.println("*CarAudioService*");
244 writer.println("\tRun in legacy mode? " + (!mUseDynamicRouting)); 257 writer.println("\tRun in legacy mode? " + (!mUseDynamicRouting));
245 writer.println("\tMaster mute? " + mAudioManager.isMasterMute()); 258 writer.println("\tPersist master mute state? " + mPersistMasterMuteState);
259 writer.println("\tMaster muted? " + mAudioManager.isMasterMute());
246 // Empty line for comfortable reading 260 // Empty line for comfortable reading
247 writer.println(); 261 writer.println();
248 if (mUseDynamicRouting) { 262 if (mUseDynamicRouting) {
@@ -283,6 +297,16 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
283 } 297 }
284 } 298 }
285 299
300 private void setMasterMute(boolean mute, int flags) {
301 mAudioManager.setMasterMute(mute, flags);
302
303 // When the master mute is turned ON, we want the playing app to get a "pause" command.
304 // When the volume is unmuted, we want to resume playback.
305 int keycode = mute ? KeyEvent.KEYCODE_MEDIA_PAUSE : KeyEvent.KEYCODE_MEDIA_PLAY;
306 mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keycode));
307 mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keycode));
308 }
309
286 private void callbackMasterMuteChange(int flags) { 310 private void callbackMasterMuteChange(int flags) {
287 for (BinderInterfaceContainer.BinderInterface<ICarVolumeCallback> callback : 311 for (BinderInterfaceContainer.BinderInterface<ICarVolumeCallback> callback :
288 mVolumeCallbackContainer.getInterfaces()) { 312 mVolumeCallbackContainer.getInterfaces()) {
@@ -292,6 +316,13 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
292 Log.e(CarLog.TAG_AUDIO, "Failed to callback onMasterMuteChanged", e); 316 Log.e(CarLog.TAG_AUDIO, "Failed to callback onMasterMuteChanged", e);
293 } 317 }
294 } 318 }
319
320 // Persists master mute state if applicable
321 if (mPersistMasterMuteState) {
322 Settings.Global.putInt(mContext.getContentResolver(),
323 CarAudioManager.VOLUME_SETTINGS_KEY_MASTER_MUTE,
324 mAudioManager.isMasterMute() ? 1 : 0);
325 }
295 } 326 }
296 327
297 /** 328 /**
@@ -688,6 +719,11 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase {
688 Preconditions.checkNotNull(patch[0], 719 Preconditions.checkNotNull(patch[0],
689 "createAudioPatch didn't provide expected single handle"); 720 "createAudioPatch didn't provide expected single handle");
690 Log.d(CarLog.TAG_AUDIO, "Audio patch created: " + patch[0]); 721 Log.d(CarLog.TAG_AUDIO, "Audio patch created: " + patch[0]);
722
723 // Ensure the initial volume on output device port
724 int groupId = getVolumeGroupIdForUsage(usage);
725 setGroupVolume(groupId, getGroupVolume(groupId), 0);
726
691 return new CarAudioPatchHandle(patch[0]); 727 return new CarAudioPatchHandle(patch[0]);
692 } 728 }
693 729
diff --git a/service/src/com/android/car/CarInputService.java b/service/src/com/android/car/CarInputService.java
index 1f67259e..2b97221e 100644
--- a/service/src/com/android/car/CarInputService.java
+++ b/service/src/com/android/car/CarInputService.java
@@ -16,7 +16,9 @@
16package com.android.car; 16package com.android.car;
17 17
18import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; 18import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
19import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
19 20
21import android.app.ActivityManager;
20import android.car.input.CarInputHandlingService; 22import android.car.input.CarInputHandlingService;
21import android.car.input.CarInputHandlingService.InputFilter; 23import android.car.input.CarInputHandlingService.InputFilter;
22import android.car.input.ICarInputListener; 24import android.car.input.ICarInputListener;
@@ -30,19 +32,18 @@ import android.os.Binder;
30import android.os.Bundle; 32import android.os.Bundle;
31import android.os.IBinder; 33import android.os.IBinder;
32import android.os.Parcel; 34import android.os.Parcel;
33import android.os.ParcelFileDescriptor;
34import android.os.RemoteException; 35import android.os.RemoteException;
35import android.os.SystemClock; 36import android.os.SystemClock;
36import android.os.UserHandle; 37import android.os.UserHandle;
37import android.provider.CallLog.Calls; 38import android.provider.CallLog.Calls;
38import android.speech.RecognizerIntent;
39import android.telecom.TelecomManager; 39import android.telecom.TelecomManager;
40import android.text.TextUtils; 40import android.text.TextUtils;
41import android.util.Log; 41import android.util.Log;
42import android.view.KeyEvent; 42import android.view.KeyEvent;
43 43
44import com.android.car.hal.InputHalService; 44import com.android.car.hal.InputHalService;
45import com.android.car.hal.VehicleHal; 45import com.android.internal.app.AssistUtils;
46import com.android.internal.app.IVoiceInteractionSessionShowCallback;
46 47
47import java.io.PrintWriter; 48import java.io.PrintWriter;
48import java.util.HashMap; 49import java.util.HashMap;
@@ -83,12 +84,30 @@ public class CarInputService implements CarServiceBase, InputHalService.InputLis
83 } 84 }
84 } 85 }
85 86
87 private IVoiceInteractionSessionShowCallback mShowCallback =
88 new IVoiceInteractionSessionShowCallback.Stub() {
89 @Override
90 public void onFailed() {
91 Log.w(CarLog.TAG_INPUT, "Failed to show VoiceInteractionSession");
92 }
93
94 @Override
95 public void onShown() {
96 if (DBG) {
97 Log.d(CarLog.TAG_INPUT, "IVoiceInteractionSessionShowCallback onShown()");
98 }
99 }
100 };
101
86 private static final boolean DBG = false; 102 private static final boolean DBG = false;
103 private static final String EXTRA_CAR_PUSH_TO_TALK =
104 "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK";
87 105
88 private final Context mContext; 106 private final Context mContext;
89 private final InputHalService mInputHalService; 107 private final InputHalService mInputHalService;
90 private final TelecomManager mTelecomManager; 108 private final TelecomManager mTelecomManager;
91 private final InputManager mInputManager; 109 private final InputManager mInputManager;
110 private final AssistUtils mAssistUtils;
92 111
93 private KeyEventListener mVoiceAssistantKeyListener; 112 private KeyEventListener mVoiceAssistantKeyListener;
94 private KeyEventListener mLongVoiceAssistantKeyListener; 113 private KeyEventListener mLongVoiceAssistantKeyListener;
@@ -153,6 +172,7 @@ public class CarInputService implements CarServiceBase, InputHalService.InputLis
153 mInputHalService = inputHalService; 172 mInputHalService = inputHalService;
154 mTelecomManager = context.getSystemService(TelecomManager.class); 173 mTelecomManager = context.getSystemService(TelecomManager.class);
155 mInputManager = context.getSystemService(InputManager.class); 174 mInputManager = context.getSystemService(InputManager.class);
175 mAssistUtils = new AssistUtils(context);
156 } 176 }
157 177
158 private synchronized void setHandledKeys(InputFilter[] handledKeys) { 178 private synchronized void setHandledKeys(InputFilter[] handledKeys) {
@@ -324,10 +344,18 @@ public class CarInputService implements CarServiceBase, InputHalService.InputLis
324 } 344 }
325 345
326 private void launchDefaultVoiceAssistantHandler() { 346 private void launchDefaultVoiceAssistantHandler() {
327 Log.i(CarLog.TAG_INPUT, "voice key, launch default intent"); 347 Log.i(CarLog.TAG_INPUT, "voice key, invoke AssistUtils");
328 Intent voiceIntent = 348
329 new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); 349 if (mAssistUtils.getAssistComponentForUser(ActivityManager.getCurrentUser()) == null) {
330 mContext.startActivityAsUser(voiceIntent, null, UserHandle.CURRENT_OR_SELF); 350 Log.w(CarLog.TAG_INPUT, "Unable to retrieve assist component for current user");
351 return;
352 }
353
354 final Bundle args = new Bundle();
355 args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true);
356
357 mAssistUtils.showSessionForActiveService(args,
358 SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, null /*activityToken*/);
331 } 359 }
332 360
333 private void handleInstrumentClusterKey(KeyEvent event) { 361 private void handleInstrumentClusterKey(KeyEvent event) {
diff --git a/service/src/com/android/car/CarLocationService.java b/service/src/com/android/car/CarLocationService.java
index 6d93b40c..a7aaf035 100644
--- a/service/src/com/android/car/CarLocationService.java
+++ b/service/src/com/android/car/CarLocationService.java
@@ -19,6 +19,7 @@ package com.android.car;
19import android.car.hardware.CarPropertyValue; 19import android.car.hardware.CarPropertyValue;
20import android.car.hardware.property.CarPropertyEvent; 20import android.car.hardware.property.CarPropertyEvent;
21import android.car.hardware.property.ICarPropertyEventListener; 21import android.car.hardware.property.ICarPropertyEventListener;
22import android.car.user.CarUserManagerHelper;
22import android.content.BroadcastReceiver; 23import android.content.BroadcastReceiver;
23import android.content.Context; 24import android.content.Context;
24import android.content.Intent; 25import android.content.Intent;
@@ -31,6 +32,7 @@ import android.os.Handler;
31import android.os.HandlerThread; 32import android.os.HandlerThread;
32import android.os.RemoteException; 33import android.os.RemoteException;
33import android.os.SystemClock; 34import android.os.SystemClock;
35import android.os.UserHandle;
34import android.util.AtomicFile; 36import android.util.AtomicFile;
35import android.util.JsonReader; 37import android.util.JsonReader;
36import android.util.JsonWriter; 38import android.util.JsonWriter;
@@ -60,6 +62,8 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
60 private static final long GRANULARITY_ONE_DAY_MS = 24 * 60 * 60 * 1000L; 62 private static final long GRANULARITY_ONE_DAY_MS = 24 * 60 * 60 * 1000L;
61 // The time-to-live for the cached location 63 // The time-to-live for the cached location
62 private static final long TTL_THIRTY_DAYS_MS = 30 * GRANULARITY_ONE_DAY_MS; 64 private static final long TTL_THIRTY_DAYS_MS = 30 * GRANULARITY_ONE_DAY_MS;
65 // The maximum number of times to try injecting a location
66 private static final int MAX_LOCATION_INJECTION_ATTEMPTS = 10;
63 67
64 // Used internally for mHandlerThread synchronization 68 // Used internally for mHandlerThread synchronization
65 private final Object mLock = new Object(); 69 private final Object mLock = new Object();
@@ -68,23 +72,26 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
68 private final CarPowerManagementService mCarPowerManagementService; 72 private final CarPowerManagementService mCarPowerManagementService;
69 private final CarPropertyService mCarPropertyService; 73 private final CarPropertyService mCarPropertyService;
70 private final CarPropertyEventListener mCarPropertyEventListener; 74 private final CarPropertyEventListener mCarPropertyEventListener;
75 private final CarUserManagerHelper mCarUserManagerHelper;
71 private int mTaskCount = 0; 76 private int mTaskCount = 0;
72 private HandlerThread mHandlerThread; 77 private HandlerThread mHandlerThread;
73 private Handler mHandler; 78 private Handler mHandler;
74 79
75 public CarLocationService(Context context, CarPowerManagementService carPowerManagementService, 80 public CarLocationService(Context context, CarPowerManagementService carPowerManagementService,
76 CarPropertyService carPropertyService) { 81 CarPropertyService carPropertyService, CarUserManagerHelper carUserManagerHelper) {
77 logd("constructed"); 82 logd("constructed");
78 mContext = context; 83 mContext = context;
79 mCarPowerManagementService = carPowerManagementService; 84 mCarPowerManagementService = carPowerManagementService;
80 mCarPropertyService = carPropertyService; 85 mCarPropertyService = carPropertyService;
81 mCarPropertyEventListener = new CarPropertyEventListener(); 86 mCarPropertyEventListener = new CarPropertyEventListener();
87 mCarUserManagerHelper = carUserManagerHelper;
82 } 88 }
83 89
84 @Override 90 @Override
85 public void init() { 91 public void init() {
86 logd("init"); 92 logd("init");
87 IntentFilter filter = new IntentFilter(); 93 IntentFilter filter = new IntentFilter();
94 filter.addAction(Intent.ACTION_USER_SWITCHED);
88 filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); 95 filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
89 filter.addAction(LocationManager.MODE_CHANGED_ACTION); 96 filter.addAction(LocationManager.MODE_CHANGED_ACTION);
90 filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION); 97 filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -107,17 +114,19 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
107 writer.println(TAG); 114 writer.println(TAG);
108 writer.println("Context: " + mContext); 115 writer.println("Context: " + mContext);
109 writer.println("CarPropertyService: " + mCarPropertyService); 116 writer.println("CarPropertyService: " + mCarPropertyService);
117 writer.println("MAX_LOCATION_INJECTION_ATTEMPTS: " + MAX_LOCATION_INJECTION_ATTEMPTS);
110 } 118 }
111 119
112 @Override 120 @Override
113 public long onPrepareShutdown(boolean shuttingDown) { 121 public long onPrepareShutdown(boolean shuttingDown) {
114 logd("onPrepareShutdown " + shuttingDown); 122 logd("onPrepareShutdown " + shuttingDown);
115 asyncOperation(() -> storeLocation()); 123 asyncOperation(() -> storeLocation());
116 return 0; 124 return 100;
117 } 125 }
118 126
119 @Override 127 @Override
120 public void onPowerOn(boolean displayOn) { } 128 public void onPowerOn(boolean displayOn) {
129 }
121 130
122 @Override 131 @Override
123 public int getWakeupTime() { 132 public int getWakeupTime() {
@@ -129,27 +138,55 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
129 logd("onReceive " + intent); 138 logd("onReceive " + intent);
130 String action = intent.getAction(); 139 String action = intent.getAction();
131 if (action == Intent.ACTION_LOCKED_BOOT_COMPLETED) { 140 if (action == Intent.ACTION_LOCKED_BOOT_COMPLETED) {
132 asyncOperation(() -> loadLocation()); 141 // If the system user is not headless, then we can inject location as soon as the
133 } else { 142 // system has completed booting.
143 if (!mCarUserManagerHelper.isHeadlessSystemUser()) {
144 logd("not headless on boot complete");
145 asyncOperation(() -> loadLocation());
146 }
147 } else if (action == Intent.ACTION_USER_SWITCHED) {
148 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
149 logd("USER_SWITCHED: " + userHandle);
150 if (mCarUserManagerHelper.isHeadlessSystemUser()
151 && userHandle > UserHandle.USER_SYSTEM) {
152 asyncOperation(() -> loadLocation());
153 }
154 } else if (action == LocationManager.MODE_CHANGED_ACTION
155 && shouldCheckLocationPermissions()) {
134 LocationManager locationManager = 156 LocationManager locationManager =
135 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 157 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
136 if (action == LocationManager.MODE_CHANGED_ACTION) { 158 boolean locationEnabled = locationManager.isLocationEnabled();
137 boolean locationEnabled = locationManager.isLocationEnabled(); 159 logd("isLocationEnabled(): " + locationEnabled);
138 logd("isLocationEnabled(): " + locationEnabled); 160 if (!locationEnabled) {
139 if (!locationEnabled) { 161 asyncOperation(() -> deleteCacheFile());
140 asyncOperation(() -> deleteCacheFile()); 162 }
141 } 163 } else if (action == LocationManager.GPS_ENABLED_CHANGE_ACTION
142 } else if (action == LocationManager.GPS_ENABLED_CHANGE_ACTION) { 164 && shouldCheckLocationPermissions()) {
143 boolean gpsEnabled = 165 LocationManager locationManager =
144 locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 166 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
145 logd("isProviderEnabled('gps'): " + gpsEnabled); 167 boolean gpsEnabled =
146 if (!gpsEnabled) { 168 locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
147 asyncOperation(() -> deleteCacheFile()); 169 logd("isProviderEnabled('gps'): " + gpsEnabled);
148 } 170 if (!gpsEnabled) {
171 asyncOperation(() -> deleteCacheFile());
149 } 172 }
150 } 173 }
151 } 174 }
152 175
176 /**
177 * Tells whether or not we should check location permissions for the sake of deleting the
178 * location cache file when permissions are lacking. If the system user is headless but the
179 * current user is still the system user, then we should not respond to a lack of location
180 * permissions.
181 */
182 private boolean shouldCheckLocationPermissions() {
183 return !(mCarUserManagerHelper.isHeadlessSystemUser()
184 && mCarUserManagerHelper.isCurrentProcessSystemUser());
185 }
186
187 /**
188 * Gets the last known location from the LocationManager and store it in a file.
189 */
153 private void storeLocation() { 190 private void storeLocation() {
154 LocationManager locationManager = 191 LocationManager locationManager =
155 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 192 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
@@ -163,44 +200,44 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
163 FileOutputStream fos = null; 200 FileOutputStream fos = null;
164 try { 201 try {
165 fos = atomicFile.startWrite(); 202 fos = atomicFile.startWrite();
166 JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8")); 203 try (JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(fos, "UTF-8"))) {
167 jsonWriter.beginObject(); 204 jsonWriter.beginObject();
168 jsonWriter.name("provider").value(location.getProvider()); 205 jsonWriter.name("provider").value(location.getProvider());
169 jsonWriter.name("latitude").value(location.getLatitude()); 206 jsonWriter.name("latitude").value(location.getLatitude());
170 jsonWriter.name("longitude").value(location.getLongitude()); 207 jsonWriter.name("longitude").value(location.getLongitude());
171 if (location.hasAltitude()) { 208 if (location.hasAltitude()) {
172 jsonWriter.name("altitude").value(location.getAltitude()); 209 jsonWriter.name("altitude").value(location.getAltitude());
173 } 210 }
174 if (location.hasSpeed()) { 211 if (location.hasSpeed()) {
175 jsonWriter.name("speed").value(location.getSpeed()); 212 jsonWriter.name("speed").value(location.getSpeed());
176 } 213 }
177 if (location.hasBearing()) { 214 if (location.hasBearing()) {
178 jsonWriter.name("bearing").value(location.getBearing()); 215 jsonWriter.name("bearing").value(location.getBearing());
179 } 216 }
180 if (location.hasAccuracy()) { 217 if (location.hasAccuracy()) {
181 jsonWriter.name("accuracy").value(location.getAccuracy()); 218 jsonWriter.name("accuracy").value(location.getAccuracy());
182 } 219 }
183 if (location.hasVerticalAccuracy()) { 220 if (location.hasVerticalAccuracy()) {
184 jsonWriter.name("verticalAccuracy").value( 221 jsonWriter.name("verticalAccuracy").value(
185 location.getVerticalAccuracyMeters()); 222 location.getVerticalAccuracyMeters());
186 } 223 }
187 if (location.hasSpeedAccuracy()) { 224 if (location.hasSpeedAccuracy()) {
188 jsonWriter.name("speedAccuracy").value( 225 jsonWriter.name("speedAccuracy").value(
189 location.getSpeedAccuracyMetersPerSecond()); 226 location.getSpeedAccuracyMetersPerSecond());
190 } 227 }
191 if (location.hasBearingAccuracy()) { 228 if (location.hasBearingAccuracy()) {
192 jsonWriter.name("bearingAccuracy").value( 229 jsonWriter.name("bearingAccuracy").value(
193 location.getBearingAccuracyDegrees()); 230 location.getBearingAccuracyDegrees());
194 } 231 }
195 if (location.isFromMockProvider()) { 232 if (location.isFromMockProvider()) {
196 jsonWriter.name("isFromMockProvider").value(true); 233 jsonWriter.name("isFromMockProvider").value(true);
234 }
235 long currentTime = location.getTime();
236 // Round the time down to only be accurate within one day.
237 jsonWriter.name("captureTime").value(
238 currentTime - currentTime % GRANULARITY_ONE_DAY_MS);
239 jsonWriter.endObject();
197 } 240 }
198 long currentTime = location.getTime();
199 // Round the time down to only be accurate within one day.
200 jsonWriter.name("captureTime").value(
201 currentTime - currentTime % GRANULARITY_ONE_DAY_MS);
202 jsonWriter.endObject();
203 jsonWriter.close();
204 atomicFile.finishWrite(fos); 241 atomicFile.finishWrite(fos);
205 } catch (IOException e) { 242 } catch (IOException e) {
206 Log.e(TAG, "Unable to write to disk", e); 243 Log.e(TAG, "Unable to write to disk", e);
@@ -209,6 +246,9 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
209 } 246 }
210 } 247 }
211 248
249 /**
250 * Reads a previously stored location and attempts to inject it into the LocationManager.
251 */
212 private void loadLocation() { 252 private void loadLocation() {
213 Location location = readLocationFromCacheFile(); 253 Location location = readLocationFromCacheFile();
214 logd("Read location from " + location.getTime()); 254 logd("Read location from " + location.getTime());
@@ -220,10 +260,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
220 long elapsedTime = SystemClock.elapsedRealtimeNanos(); 260 long elapsedTime = SystemClock.elapsedRealtimeNanos();
221 location.setElapsedRealtimeNanos(elapsedTime); 261 location.setElapsedRealtimeNanos(elapsedTime);
222 if (location.isComplete()) { 262 if (location.isComplete()) {
223 LocationManager locationManager = 263 injectLocation(location, 1);
224 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
225 boolean success = locationManager.injectLocation(location);
226 logd("Injected location " + location + " with result " + success);
227 } 264 }
228 } 265 }
229 } 266 }
@@ -231,8 +268,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
231 private Location readLocationFromCacheFile() { 268 private Location readLocationFromCacheFile() {
232 Location location = new Location((String) null); 269 Location location = new Location((String) null);
233 AtomicFile atomicFile = new AtomicFile(mContext.getFileStreamPath(FILENAME)); 270 AtomicFile atomicFile = new AtomicFile(mContext.getFileStreamPath(FILENAME));
234 try { 271 try (FileInputStream fis = atomicFile.openRead()) {
235 FileInputStream fis = atomicFile.openRead();
236 JsonReader reader = new JsonReader(new InputStreamReader(fis, "UTF-8")); 272 JsonReader reader = new JsonReader(new InputStreamReader(fis, "UTF-8"));
237 reader.beginObject(); 273 reader.beginObject();
238 while (reader.hasNext()) { 274 while (reader.hasNext()) {
@@ -266,7 +302,6 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
266 } 302 }
267 } 303 }
268 reader.endObject(); 304 reader.endObject();
269 fis.close();
270 deleteCacheFile(); 305 deleteCacheFile();
271 } catch (FileNotFoundException e) { 306 } catch (FileNotFoundException e) {
272 Log.d(TAG, "Location cache file not found."); 307 Log.d(TAG, "Location cache file not found.");
@@ -283,8 +318,33 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
283 mContext.deleteFile(FILENAME); 318 mContext.deleteFile(FILENAME);
284 } 319 }
285 320
321 /**
322 * Attempts to inject the location multiple times in case the LocationManager was not fully
323 * initialized or has not updated its handle to the current user yet.
324 */
325 private void injectLocation(Location location, int attemptCount) {
326 LocationManager locationManager =
327 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
328 boolean success = locationManager.injectLocation(location);
329 logd("Injected location " + location + " with result " + success + " on attempt "
330 + attemptCount);
331 if (success) {
332 return;
333 } else if (attemptCount <= MAX_LOCATION_INJECTION_ATTEMPTS) {
334 asyncOperation(() -> {
335 injectLocation(location, attemptCount + 1);
336 }, 200 * attemptCount);
337 } else {
338 logd("No location injected.");
339 }
340 }
341
286 @VisibleForTesting 342 @VisibleForTesting
287 void asyncOperation(Runnable operation) { 343 void asyncOperation(Runnable operation) {
344 asyncOperation(operation, 0);
345 }
346
347 private void asyncOperation(Runnable operation, long delayMillis) {
288 synchronized (mLock) { 348 synchronized (mLock) {
289 // Create a new HandlerThread if this is the first task to queue. 349 // Create a new HandlerThread if this is the first task to queue.
290 if (++mTaskCount == 1) { 350 if (++mTaskCount == 1) {
@@ -293,7 +353,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
293 mHandler = new Handler(mHandlerThread.getLooper()); 353 mHandler = new Handler(mHandlerThread.getLooper());
294 } 354 }
295 } 355 }
296 mHandler.post(() -> { 356 mHandler.postDelayed(() -> {
297 try { 357 try {
298 operation.run(); 358 operation.run();
299 } finally { 359 } finally {
@@ -306,7 +366,7 @@ public class CarLocationService extends BroadcastReceiver implements CarServiceB
306 } 366 }
307 } 367 }
308 } 368 }
309 }); 369 }, delayMillis);
310 } 370 }
311 371
312 private static void logd(String msg) { 372 private static void logd(String msg) {
diff --git a/service/src/com/android/car/CarNightService.java b/service/src/com/android/car/CarNightService.java
index e43ed3d8..dcf75b55 100644
--- a/service/src/com/android/car/CarNightService.java
+++ b/service/src/com/android/car/CarNightService.java
@@ -72,23 +72,27 @@ public class CarNightService implements CarServiceBase {
72 CarPropertyValue value = event.getCarPropertyValue(); 72 CarPropertyValue value = event.getCarPropertyValue();
73 if (value.getPropertyId() == VehicleProperty.NIGHT_MODE) { 73 if (value.getPropertyId() == VehicleProperty.NIGHT_MODE) {
74 boolean nightMode = (Boolean) value.getValue(); 74 boolean nightMode = (Boolean) value.getValue();
75 if (nightMode) { 75 setNightMode(nightMode);
76 mNightSetting = UiModeManager.MODE_NIGHT_YES;
77 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent NIGHT");
78 } else {
79 mNightSetting = UiModeManager.MODE_NIGHT_NO;
80 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent DAY");
81 }
82 if (mUiModeManager != null && (mForcedMode == FORCED_SENSOR_MODE)) {
83 mUiModeManager.setNightMode(mNightSetting);
84 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent APPLIED");
85 } else {
86 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent IGNORED");
87 }
88 } 76 }
89 } 77 }
90 } 78 }
91 79
80 private synchronized void setNightMode(boolean nightMode) {
81 if (nightMode) {
82 mNightSetting = UiModeManager.MODE_NIGHT_YES;
83 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent NIGHT");
84 } else {
85 mNightSetting = UiModeManager.MODE_NIGHT_NO;
86 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent DAY");
87 }
88 if (mUiModeManager != null && (mForcedMode == FORCED_SENSOR_MODE)) {
89 mUiModeManager.setNightMode(mNightSetting);
90 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent APPLIED");
91 } else {
92 if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent IGNORED");
93 }
94 }
95
92 public synchronized int forceDayNightMode(@DayNightSensorMode int mode) { 96 public synchronized int forceDayNightMode(@DayNightSensorMode int mode) {
93 if (mUiModeManager == null) { 97 if (mUiModeManager == null) {
94 return -1; 98 return -1;
@@ -131,6 +135,15 @@ public class CarNightService implements CarServiceBase {
131 } 135 }
132 mCarPropertyService.registerListener(VehicleProperty.NIGHT_MODE, 0, 136 mCarPropertyService.registerListener(VehicleProperty.NIGHT_MODE, 0,
133 mICarPropertyEventListener); 137 mICarPropertyEventListener);
138 CarPropertyValue propertyValue = mCarPropertyService.getProperty(
139 VehicleProperty.NIGHT_MODE, 0);
140 if (propertyValue != null && propertyValue.getTimestamp() != 0) {
141 setNightMode((Boolean) propertyValue.getValue());
142 } else {
143 Log.w(CarLog.TAG_SENSOR, "Failed to get value of NIGHT_MODE");
144 // Initial in Night Mode
145 setNightMode(true);
146 }
134 } 147 }
135 148
136 @Override 149 @Override
diff --git a/service/src/com/android/car/CarPropertyService.java b/service/src/com/android/car/CarPropertyService.java
index 68c4b8cb..a01d4b87 100644
--- a/service/src/com/android/car/CarPropertyService.java
+++ b/service/src/com/android/car/CarPropertyService.java
@@ -137,6 +137,13 @@ public class CarPropertyService extends ICarProperty.Stub
137 137
138 @Override 138 @Override
139 public void init() { 139 public void init() {
140 if (mConfigs == null) {
141 // Cache the configs list to avoid subsequent binder calls
142 mConfigs = mHal.getPropertyList();
143 if (DBG) {
144 Log.d(TAG, "cache CarPropertyConfigs " + mConfigs.size());
145 }
146 }
140 } 147 }
141 148
142 @Override 149 @Override
@@ -197,14 +204,20 @@ public class CarPropertyService extends ICarProperty.Stub
197 mHal.subscribeProperty(propId, rate); 204 mHal.subscribeProperty(propId, rate);
198 } 205 }
199 } 206 }
200
201 // Send the latest value(s) to the registering listener only 207 // Send the latest value(s) to the registering listener only
202 List<CarPropertyEvent> events = new LinkedList<CarPropertyEvent>(); 208 List<CarPropertyEvent> events = new LinkedList<CarPropertyEvent>();
203 for (int areaId : mConfigs.get(propId).getAreaIds()) { 209 if (mConfigs.get(propId).isGlobalProperty()) {
204 CarPropertyValue value = mHal.getProperty(propId, areaId); 210 CarPropertyValue value = mHal.getProperty(propId, 0);
205 CarPropertyEvent event = new CarPropertyEvent( 211 CarPropertyEvent event = new CarPropertyEvent(
206 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value); 212 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
207 events.add(event); 213 events.add(event);
214 } else {
215 for (int areaId : mConfigs.get(propId).getAreaIds()) {
216 CarPropertyValue value = mHal.getProperty(propId, areaId);
217 CarPropertyEvent event = new CarPropertyEvent(
218 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
219 events.add(event);
220 }
208 } 221 }
209 try { 222 try {
210 listener.onEvent(events); 223 listener.onEvent(events);
@@ -280,10 +293,6 @@ public class CarPropertyService extends ICarProperty.Stub
280 @Override 293 @Override
281 public List<CarPropertyConfig> getPropertyList() { 294 public List<CarPropertyConfig> getPropertyList() {
282 List<CarPropertyConfig> returnList = new ArrayList<CarPropertyConfig>(); 295 List<CarPropertyConfig> returnList = new ArrayList<CarPropertyConfig>();
283 if (mConfigs == null) {
284 // Cache the configs list to avoid subsequent binder calls
285 mConfigs = mHal.getPropertyList();
286 }
287 for (CarPropertyConfig c : mConfigs.values()) { 296 for (CarPropertyConfig c : mConfigs.values()) {
288 if (ICarImpl.hasPermission(mContext, mHal.getReadPermission(c.getPropertyId()))) { 297 if (ICarImpl.hasPermission(mContext, mHal.getReadPermission(c.getPropertyId()))) {
289 // Only add properties the list if the process has permissions to read it 298 // Only add properties the list if the process has permissions to read it
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index 23c4d32a..b90939b3 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -27,11 +27,17 @@ import android.car.hardware.CarPropertyValue;
27import android.car.hardware.property.CarPropertyEvent; 27import android.car.hardware.property.CarPropertyEvent;
28import android.car.hardware.property.ICarPropertyEventListener; 28import android.car.hardware.property.ICarPropertyEventListener;
29import android.content.Context; 29import android.content.Context;
30import android.content.pm.PackageManager;
30import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 31import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
32import android.os.Binder;
33import android.os.Build;
31import android.os.IBinder; 34import android.os.IBinder;
35import android.os.Process;
32import android.os.RemoteException; 36import android.os.RemoteException;
33import android.util.Log; 37import android.util.Log;
34 38
39import com.android.internal.annotations.GuardedBy;
40
35import org.xmlpull.v1.XmlPullParserException; 41import org.xmlpull.v1.XmlPullParserException;
36 42
37import java.io.IOException; 43import java.io.IOException;
@@ -60,6 +66,9 @@ public class CarUxRestrictionsManagerService extends ICarUxRestrictionsManager.S
60 private CarUxRestrictions mCurrentUxRestrictions; 66 private CarUxRestrictions mCurrentUxRestrictions;
61 private float mCurrentMovingSpeed; 67 private float mCurrentMovingSpeed;
62 private boolean mFallbackToDefaults; 68 private boolean mFallbackToDefaults;
69 // Flag to disable broadcasting UXR changes - for development purposes
70 @GuardedBy("this")
71 private boolean mUxRChangeBroadcastEnabled = true;
63 // For dumpsys logging 72 // For dumpsys logging
64 private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>(); 73 private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>();
65 74
@@ -224,6 +233,42 @@ public class CarUxRestrictionsManagerService extends ICarUxRestrictionsManager.S
224 } 233 }
225 234
226 /** 235 /**
236 * Enable/disable UX restrictions change broadcast blocking.
237 * Setting this to true will stop broadcasts of UX restriction change to listeners.
238 * This method works only on debug builds and the caller of this method needs to have the same
239 * signature of the car service.
240 *
241 */
242 public synchronized void setUxRChangeBroadcastEnabled(boolean enable) {
243 if (!isDebugBuild()) {
244 Log.e(TAG, "Cannot set UX restriction change broadcast.");
245 return;
246 }
247 // Check if the caller has the same signature as that of the car service.
248 if (mContext.getPackageManager().checkSignatures(Process.myUid(), Binder.getCallingUid())
249 != PackageManager.SIGNATURE_MATCH) {
250 throw new SecurityException(
251 "Caller " + mContext.getPackageManager().getNameForUid(Binder.getCallingUid())
252 + " does not have the right signature");
253 }
254 if (enable) {
255 // if enabling it back, send the current restrictions
256 mUxRChangeBroadcastEnabled = enable;
257 handleDispatchUxRestrictions(mDrivingStateService.getCurrentDrivingState().eventValue,
258 getCurrentSpeed());
259 } else {
260 // fake parked state, so if the system is currently restricted, the restrictions are
261 // relaxed.
262 handleDispatchUxRestrictions(CarDrivingStateEvent.DRIVING_STATE_PARKED, 0);
263 mUxRChangeBroadcastEnabled = enable;
264 }
265 }
266
267 private boolean isDebugBuild() {
268 return Build.IS_USERDEBUG || Build.IS_ENG;
269 }
270
271 /**
227 * Class that holds onto client related information - listener interface, process that hosts the 272 * Class that holds onto client related information - listener interface, process that hosts the
228 * binder object etc. 273 * binder object etc.
229 * It also registers for death notifications of the host. 274 * It also registers for death notifications of the host.
@@ -282,6 +327,9 @@ public class CarUxRestrictionsManagerService extends ICarUxRestrictionsManager.S
282 writer.println( 327 writer.println(
283 "Requires DO? " + mCurrentUxRestrictions.isRequiresDistractionOptimization()); 328 "Requires DO? " + mCurrentUxRestrictions.isRequiresDistractionOptimization());
284 writer.println("Current UXR: " + mCurrentUxRestrictions.getActiveRestrictions()); 329 writer.println("Current UXR: " + mCurrentUxRestrictions.getActiveRestrictions());
330 if (isDebugBuild()) {
331 writer.println("mUxRChangeBroadcastEnabled? " + mUxRChangeBroadcastEnabled);
332 }
285 mHelper.dump(writer); 333 mHelper.dump(writer);
286 writer.println("UX Restriction change log:"); 334 writer.println("UX Restriction change log:");
287 for (Utils.TransitionLog tlog : mTransitionLogs) { 335 for (Utils.TransitionLog tlog : mTransitionLogs) {
@@ -377,6 +425,11 @@ public class CarUxRestrictionsManagerService extends ICarUxRestrictionsManager.S
377 */ 425 */
378 private synchronized void handleDispatchUxRestrictions(@CarDrivingState int currentDrivingState, 426 private synchronized void handleDispatchUxRestrictions(@CarDrivingState int currentDrivingState,
379 float speed) { 427 float speed) {
428 if (isDebugBuild() && !mUxRChangeBroadcastEnabled) {
429 Log.d(TAG, "Not dispatching UX Restriction due to setting");
430 return;
431 }
432
380 CarUxRestrictions uxRestrictions; 433 CarUxRestrictions uxRestrictions;
381 // Get UX restrictions from the parsed configuration XML or fall back to defaults if not 434 // Get UX restrictions from the parsed configuration XML or fall back to defaults if not
382 // available. 435 // available.
@@ -428,7 +481,7 @@ public class CarUxRestrictionsManagerService extends ICarUxRestrictionsManager.S
428 break; 481 break;
429 case CarDrivingStateEvent.DRIVING_STATE_IDLING: 482 case CarDrivingStateEvent.DRIVING_STATE_IDLING:
430 restrictions = CarUxRestrictions.UX_RESTRICTIONS_BASELINE; 483 restrictions = CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
431 requiresOpt = true; 484 requiresOpt = false;
432 break; 485 break;
433 case CarDrivingStateEvent.DRIVING_STATE_MOVING: 486 case CarDrivingStateEvent.DRIVING_STATE_MOVING:
434 default: 487 default:
diff --git a/service/src/com/android/car/CarUxRestrictionsServiceHelper.java b/service/src/com/android/car/CarUxRestrictionsServiceHelper.java
index 236d9154..348d3be5 100644
--- a/service/src/com/android/car/CarUxRestrictionsServiceHelper.java
+++ b/service/src/com/android/car/CarUxRestrictionsServiceHelper.java
@@ -83,6 +83,10 @@ import java.util.Map;
83 public boolean loadUxRestrictionsFromXml() throws IOException, XmlPullParserException { 83 public boolean loadUxRestrictionsFromXml() throws IOException, XmlPullParserException {
84 mRestrictionsMap.clear(); 84 mRestrictionsMap.clear();
85 XmlResourceParser parser = mContext.getResources().getXml(mXmlResource); 85 XmlResourceParser parser = mContext.getResources().getXml(mXmlResource);
86 if (parser == null) {
87 Log.e(TAG, "Invalid Xml resource");
88 return false;
89 }
86 AttributeSet attrs = Xml.asAttributeSet(parser); 90 AttributeSet attrs = Xml.asAttributeSet(parser);
87 int type; 91 int type;
88 // Traverse till we get to the first tag 92 // Traverse till we get to the first tag
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index ce5ea750..67c50a75 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -36,6 +36,7 @@ import android.util.Slog;
36import android.util.TimingsTraceLog; 36import android.util.TimingsTraceLog;
37 37
38import com.android.car.cluster.InstrumentClusterService; 38import com.android.car.cluster.InstrumentClusterService;
39import com.android.car.garagemode.GarageModeService;
39import com.android.car.hal.VehicleHal; 40import com.android.car.hal.VehicleHal;
40import com.android.car.internal.FeatureConfiguration; 41import com.android.car.internal.FeatureConfiguration;
41import com.android.car.pm.CarPackageManagerService; 42import com.android.car.pm.CarPackageManagerService;
@@ -122,8 +123,6 @@ public class ICarImpl extends ICar.Stub {
122 mCarInputService = new CarInputService(serviceContext, mHal.getInputHal()); 123 mCarInputService = new CarInputService(serviceContext, mHal.getInputHal());
123 mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService); 124 mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService);
124 mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService); 125 mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService);
125 mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService,
126 mCarPropertyService);
127 mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService); 126 mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService);
128 mCarAudioService = new CarAudioService(serviceContext); 127 mCarAudioService = new CarAudioService(serviceContext);
129 mCarNightService = new CarNightService(serviceContext, mCarPropertyService); 128 mCarNightService = new CarNightService(serviceContext, mCarPropertyService);
@@ -142,6 +141,8 @@ public class ICarImpl extends ICar.Stub {
142 mCarConfigurationService = 141 mCarConfigurationService =
143 new CarConfigurationService(serviceContext, new JsonReaderImpl()); 142 new CarConfigurationService(serviceContext, new JsonReaderImpl());
144 mUserManagerHelper = new CarUserManagerHelper(serviceContext); 143 mUserManagerHelper = new CarUserManagerHelper(serviceContext);
144 mCarLocationService = new CarLocationService(mContext, mCarPowerManagementService,
145 mCarPropertyService, mUserManagerHelper);
145 146
146 // Be careful with order. Service depending on other service should be inited later. 147 // Be careful with order. Service depending on other service should be inited later.
147 List<CarServiceBase> allServices = new ArrayList<>(); 148 List<CarServiceBase> allServices = new ArrayList<>();
@@ -152,7 +153,6 @@ public class ICarImpl extends ICar.Stub {
152 allServices.add(mCarUXRestrictionsService); 153 allServices.add(mCarUXRestrictionsService);
153 allServices.add(mCarPackageManagerService); 154 allServices.add(mCarPackageManagerService);
154 allServices.add(mCarInputService); 155 allServices.add(mCarInputService);
155 allServices.add(mCarLocationService);
156 allServices.add(mGarageModeService); 156 allServices.add(mGarageModeService);
157 allServices.add(mAppFocusService); 157 allServices.add(mAppFocusService);
158 allServices.add(mCarAudioService); 158 allServices.add(mCarAudioService);
@@ -173,6 +173,7 @@ public class ICarImpl extends ICar.Stub {
173 allServices.add(mCarUserService); 173 allServices.add(mCarUserService);
174 } 174 }
175 175
176 allServices.add(mCarLocationService);
176 mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]); 177 mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
177 } 178 }
178 179
diff --git a/service/src/com/android/car/garagemode/GarageModePolicy.java b/service/src/com/android/car/garagemode/GarageModePolicy.java
new file mode 100644
index 00000000..be7b7131
--- /dev/null
+++ b/service/src/com/android/car/garagemode/GarageModePolicy.java
@@ -0,0 +1,157 @@
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
17package com.android.car.garagemode;
18
19import android.content.Context;
20import android.util.Log;
21
22import com.android.car.R;
23
24import java.util.HashMap;
25import java.util.LinkedList;
26import java.util.List;
27import java.util.Map;
28
29/**
30 * Default garage mode policy.
31 *
32 * The first wake up time is set to be 1am the next day. And it keeps waking up every day for a
33 * week. After that, wake up every 7 days for a month, and wake up every 30 days thereafter.
34 */
35public class GarageModePolicy {
36 private static final String TAG = "GarageModePolicy";
37 private static final Map<Character, Integer> TIME_UNITS_LOOKUP;
38 static {
39 TIME_UNITS_LOOKUP = new HashMap<>();
40 TIME_UNITS_LOOKUP.put('m', 60);
41 TIME_UNITS_LOOKUP.put('h', 3600);
42 TIME_UNITS_LOOKUP.put('d', 86400);
43 }
44
45 private LinkedList<WakeupInterval> mWakeupIntervals;
46
47 public GarageModePolicy(String[] policy) {
48 mWakeupIntervals = parsePolicy(policy);
49 }
50
51 /**
52 * Initializes GarageModePolicy from config_garageModeCadence resource array.
53 * @param context to access resources
54 * @return GarageModePolicy instance, created from values in resources
55 */
56 public static GarageModePolicy initFromResources(Context context) {
57 return new GarageModePolicy(
58 context.getResources().getStringArray(R.array.config_garageModeCadence));
59 }
60
61 /**
62 * Returns the interval in milliseconds, which defines next wake up time.
63 * @param index amount of times system woken up
64 * @return the interval in milliseconds
65 */
66 public int getNextWakeUpInterval(int index) {
67 if (mWakeupIntervals.size() == 0) {
68 Log.e(TAG, "No wake up policy configuration was loaded.");
69 return 0;
70 }
71
72 for (WakeupInterval wakeupTime : mWakeupIntervals) {
73 if (index < wakeupTime.getNumAttempts()) {
74 return wakeupTime.getWakeupInterval();
75 }
76 index -= wakeupTime.getNumAttempts();
77 }
78 Log.w(TAG, "No more garage mode wake ups scheduled; been sleeping too long.");
79 return 0;
80 }
81
82 /**
83 * Get list of {@link com.android.car.garagemode.WakeupInterval}s in this policy
84 * @return list as List\<WakeupInterval\>
85 */
86 public List<WakeupInterval> getWakeupIntervals() {
87 return mWakeupIntervals;
88 }
89
90 private LinkedList<WakeupInterval> parsePolicy(String[] policy) {
91 LinkedList<WakeupInterval> intervals = new LinkedList<>();
92 if (policy == null || policy.length == 0) {
93 Log.e(TAG, "Trying to parse empty policies!");
94 return intervals;
95 }
96
97 for (String rule : policy) {
98 WakeupInterval interval = parseRule(rule);
99 if (interval == null) {
100 Log.e(TAG, "Invalid Policy! This rule has bad format: " + rule);
101 return new LinkedList<>();
102 }
103 intervals.add(interval);
104 }
105 return intervals;
106 }
107
108 private WakeupInterval parseRule(String rule) {
109 String[] str = rule.split(",");
110
111 if (str.length != 2) {
112 Log.e(TAG, "Policy has bad format: " + rule);
113 return null;
114 }
115
116 String intervalStr = str[0];
117 String timesStr = str[1];
118
119 if (intervalStr.isEmpty() || timesStr.isEmpty()) {
120 Log.e(TAG, "One of the values is empty. Please check format: " + rule);
121 return null;
122 }
123
124 char unit = intervalStr.charAt(intervalStr.length() - 1);
125
126 // Removing last letter extension from string
127 intervalStr = intervalStr.substring(0, intervalStr.length() - 1);
128
129 int interval, times;
130 try {
131 interval = Integer.parseInt(intervalStr);
132 times = Integer.parseInt(timesStr);
133 } catch (NumberFormatException ex) {
134 Log.d(TAG, "Invalid input Rule for interval " + rule);
135 return null;
136 }
137
138 if (!TIME_UNITS_LOOKUP.containsKey(unit)) {
139 Log.e(TAG, "Time units map does not contain extension " + unit);
140 return null;
141 }
142
143 if (interval <= 0) {
144 Log.e(TAG, "Wake up policy time must be > 0!" + interval);
145 return null;
146 }
147
148 if (times <= 0) {
149 Log.e(TAG, "Wake up attempts in policy must be > 0!" + times);
150 return null;
151 }
152
153 interval *= TIME_UNITS_LOOKUP.get(unit);
154
155 return new WakeupInterval(interval, times);
156 }
157}
diff --git a/service/src/com/android/car/GarageModeService.java b/service/src/com/android/car/garagemode/GarageModeService.java
index 0ccddf1a..c9c34ddf 100644
--- a/service/src/com/android/car/GarageModeService.java
+++ b/service/src/com/android/car/garagemode/GarageModeService.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2015 The Android Open Source Project 2 * Copyright (C) 2018 The Android Open Source Project
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -13,9 +13,13 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16package com.android.car;
17 16
18import android.car.CarApiUtil; 17package com.android.car.garagemode;
18
19import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_ENABLED_URI;
20import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_MAINTENANCE_WINDOW_URI;
21import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_WAKE_UP_TIME_URI;
22
19import android.car.settings.CarSettings; 23import android.car.settings.CarSettings;
20import android.car.settings.GarageModeSettingsObserver; 24import android.car.settings.GarageModeSettingsObserver;
21import android.content.Context; 25import android.content.Context;
@@ -32,32 +36,28 @@ import android.preference.PreferenceManager;
32import android.provider.Settings; 36import android.provider.Settings;
33import android.util.Log; 37import android.util.Log;
34 38
39import com.android.car.CarPowerManagementService;
40import com.android.car.CarServiceBase;
41import com.android.car.DeviceIdleControllerWrapper;
35import com.android.internal.annotations.GuardedBy; 42import com.android.internal.annotations.GuardedBy;
36import com.android.internal.annotations.VisibleForTesting; 43import com.android.internal.annotations.VisibleForTesting;
37 44
38import java.io.PrintWriter; 45import java.io.PrintWriter;
39import java.lang.RuntimeException;
40import java.util.Map;
41import java.util.HashMap;
42
43import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_ENABLED_URI;
44import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_MAINTENANCE_WINDOW_URI;
45import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_WAKE_UP_TIME_URI;
46 46
47/** 47/**
48 * Controls car garage mode. 48 * Controls car garage mode.
49 * 49 *
50 * Car garage mode is a time window for the car to do maintenance work when the car is not in use. 50 * Car garage mode is a time window for the car to do maintenance work when the car is not in use.
51 * The {@link com.android.car.GarageModeService} interacts with {@link com.android.car.CarPowerManagementService} 51 * The {@link com.android.car.garagemode.GarageModeService} interacts with
52 * to start and end garage mode. A {@link com.android.car.GarageModeService.GarageModePolicy} defines 52 * {@link com.android.car.CarPowerManagementService} to start and end garage mode.
53 * when the garage mode should start and how long it should last. 53 * A {@link com.android.car.garagemode.GarageModePolicy} defines when the garage mode should
54 * start and how long it should last.
54 */ 55 */
55public class GarageModeService implements CarServiceBase, 56public class GarageModeService implements CarServiceBase,
56 CarPowerManagementService.PowerEventProcessingHandler, 57 CarPowerManagementService.PowerEventProcessingHandler,
57 CarPowerManagementService.PowerServiceEventListener, 58 CarPowerManagementService.PowerServiceEventListener,
58 DeviceIdleControllerWrapper.DeviceMaintenanceActivityListener { 59 DeviceIdleControllerWrapper.DeviceMaintenanceActivityListener {
59 private static String TAG = "GarageModeService"; 60 private static final String TAG = "GarageModeService";
60 private static final boolean DBG = false;
61 61
62 private static final int MSG_EXIT_GARAGE_MODE_EARLY = 0; 62 private static final int MSG_EXIT_GARAGE_MODE_EARLY = 0;
63 private static final int MSG_WRITE_TO_PREF = 1; 63 private static final int MSG_WRITE_TO_PREF = 1;
@@ -65,7 +65,7 @@ public class GarageModeService implements CarServiceBase,
65 private static final String KEY_GARAGE_MODE_INDEX = "garage_mode_index"; 65 private static final String KEY_GARAGE_MODE_INDEX = "garage_mode_index";
66 66
67 // wait for 10 seconds to allow maintenance activities to start (e.g., connecting to wifi). 67 // wait for 10 seconds to allow maintenance activities to start (e.g., connecting to wifi).
68 protected static final int MAINTENANCE_ACTIVITY_START_GRACE_PERIOUD = 10 * 1000; 68 protected static final int MAINTENANCE_ACTIVITY_START_GRACE_PERIOD = 10 * 1000;
69 69
70 private final CarPowerManagementService mPowerManagementService; 70 private final CarPowerManagementService mPowerManagementService;
71 protected final Context mContext; 71 protected final Context mContext;
@@ -96,7 +96,7 @@ public class GarageModeService implements CarServiceBase,
96 private final GarageModeHandler mHandler; 96 private final GarageModeHandler mHandler;
97 97
98 private class GarageModeHandler extends Handler { 98 private class GarageModeHandler extends Handler {
99 public GarageModeHandler(Looper looper) { 99 GarageModeHandler(Looper looper) {
100 super(looper); 100 super(looper);
101 } 101 }
102 102
@@ -139,21 +139,23 @@ public class GarageModeService implements CarServiceBase,
139 139
140 @Override 140 @Override
141 public void init() { 141 public void init() {
142 init(mContext.getResources().getStringArray(R.array.config_garageModeCadence), 142 init(
143 GarageModePolicy.initFromResources(mContext),
143 PreferenceManager.getDefaultSharedPreferences( 144 PreferenceManager.getDefaultSharedPreferences(
144 mContext.createDeviceProtectedStorageContext())); 145 mContext.createDeviceProtectedStorageContext()));
145 } 146 }
146 147
147 @VisibleForTesting 148 @VisibleForTesting
148 protected void init(String[] policyArray, SharedPreferences prefs) { 149 void init(GarageModePolicy policy, SharedPreferences prefs) {
149 logd("init GarageMode"); 150 Log.d(TAG, "initializing GarageMode");
150 mSharedPreferences = prefs; 151 mSharedPreferences = prefs;
152 mPolicy = policy;
151 final int index = mSharedPreferences.getInt(KEY_GARAGE_MODE_INDEX, 0); 153 final int index = mSharedPreferences.getInt(KEY_GARAGE_MODE_INDEX, 0);
152 synchronized (this) { 154 synchronized (this) {
153 mMaintenanceActive = mDeviceIdleController.startTracking(this); 155 mMaintenanceActive = mDeviceIdleController.startTracking(this);
154 mGarageModeIndex = index; 156 mGarageModeIndex = index;
155 readPolicyLocked(policyArray); 157 readFromSettingsLocked(
156 readFromSettingsLocked(CarSettings.Global.KEY_GARAGE_MODE_MAINTENANCE_WINDOW, 158 CarSettings.Global.KEY_GARAGE_MODE_MAINTENANCE_WINDOW,
157 CarSettings.Global.KEY_GARAGE_MODE_ENABLED, 159 CarSettings.Global.KEY_GARAGE_MODE_ENABLED,
158 CarSettings.Global.KEY_GARAGE_MODE_WAKE_UP_TIME); 160 CarSettings.Global.KEY_GARAGE_MODE_WAKE_UP_TIME);
159 } 161 }
@@ -167,7 +169,7 @@ public class GarageModeService implements CarServiceBase,
167 169
168 @Override 170 @Override
169 public void release() { 171 public void release() {
170 logd("release GarageModeService"); 172 Log.d(TAG, "releasing GarageModeService");
171 mDeviceIdleController.stopTracking(); 173 mDeviceIdleController.stopTracking();
172 mContentObserver.unregister(); 174 mContentObserver.unregister();
173 } 175 }
@@ -184,18 +186,18 @@ public class GarageModeService implements CarServiceBase,
184 public long onPrepareShutdown(boolean shuttingDown) { 186 public long onPrepareShutdown(boolean shuttingDown) {
185 // this is the beginning of each garage mode. 187 // this is the beginning of each garage mode.
186 synchronized (this) { 188 synchronized (this) {
187 logd("onPrePowerEvent " + shuttingDown); 189 Log.d(TAG, "onPrepareShutdown is triggered. System is shutting down=" + shuttingDown);
188 mInGarageMode = true; 190 mInGarageMode = true;
189 mGarageModeIndex++; 191 mGarageModeIndex++;
190 mHandler.removeMessages(MSG_EXIT_GARAGE_MODE_EARLY); 192 mHandler.removeMessages(MSG_EXIT_GARAGE_MODE_EARLY);
191 if (!mMaintenanceActive) { 193 if (!mMaintenanceActive) {
192 mHandler.sendMessageDelayed( 194 mHandler.sendMessageDelayed(
193 mHandler.obtainMessage(MSG_EXIT_GARAGE_MODE_EARLY), 195 mHandler.obtainMessage(MSG_EXIT_GARAGE_MODE_EARLY),
194 MAINTENANCE_ACTIVITY_START_GRACE_PERIOUD); 196 MAINTENANCE_ACTIVITY_START_GRACE_PERIOD);
195 } 197 }
196 // We always reserve the maintenance window first. If later, we found no 198 // We always reserve the maintenance window first. If later, we found no
197 // maintenance work active, we will exit garage mode early after 199 // maintenance work active, we will exit garage mode early after
198 // MAINTENANCE_ACTIVITY_START_GRACE_PERIOUD 200 // MAINTENANCE_ACTIVITY_START_GRACE_PERIOD
199 return mMaintenanceWindow; 201 return mMaintenanceWindow;
200 } 202 }
201 } 203 }
@@ -203,7 +205,7 @@ public class GarageModeService implements CarServiceBase,
203 @Override 205 @Override
204 public void onPowerOn(boolean displayOn) { 206 public void onPowerOn(boolean displayOn) {
205 synchronized (this) { 207 synchronized (this) {
206 logd("onPowerOn: " + displayOn); 208 Log.d(TAG, "onPowerOn: " + displayOn);
207 if (displayOn) { 209 if (displayOn) {
208 // the car is use now. reset the garage mode counter. 210 // the car is use now. reset the garage mode counter.
209 mGarageModeIndex = 0; 211 mGarageModeIndex = 0;
@@ -217,7 +219,7 @@ public class GarageModeService implements CarServiceBase,
217 if (!mGarageModeEnabled) { 219 if (!mGarageModeEnabled) {
218 return 0; 220 return 0;
219 } 221 }
220 return mPolicy.getNextWakeUpTime(mGarageModeIndex); 222 return mPolicy.getNextWakeUpInterval(mGarageModeIndex);
221 } 223 }
222 } 224 }
223 225
@@ -241,12 +243,6 @@ public class GarageModeService implements CarServiceBase,
241 } 243 }
242 } 244 }
243 245
244 @GuardedBy("this")
245 private void readPolicyLocked(String[] policyArray) {
246 logd("readPolicy");
247 mPolicy = new GarageModePolicy(policyArray);
248 }
249
250 private void writeToPref(int index) { 246 private void writeToPref(int index) {
251 SharedPreferences.Editor editor = mSharedPreferences.edit(); 247 SharedPreferences.Editor editor = mSharedPreferences.edit();
252 editor.putInt(KEY_GARAGE_MODE_INDEX, index); 248 editor.putInt(KEY_GARAGE_MODE_INDEX, index);
@@ -257,7 +253,7 @@ public class GarageModeService implements CarServiceBase,
257 public void onMaintenanceActivityChanged(boolean active) { 253 public void onMaintenanceActivityChanged(boolean active) {
258 boolean shouldReportCompletion = false; 254 boolean shouldReportCompletion = false;
259 synchronized (this) { 255 synchronized (this) {
260 logd("onMaintenanceActivityChanged: " + active); 256 Log.d(TAG, "onMaintenanceActivityChanged: " + active);
261 mMaintenanceActive = active; 257 mMaintenanceActive = active;
262 if (!mInGarageMode) { 258 if (!mInGarageMode) {
263 return; 259 return;
@@ -277,111 +273,11 @@ public class GarageModeService implements CarServiceBase,
277 } 273 }
278 } 274 }
279 275
280 static final class WakeupTime {
281 int mWakeupTime;
282 int mNumAttempts;
283
284 WakeupTime(int wakeupTime, int numAttempts) {
285 mWakeupTime = wakeupTime;
286 mNumAttempts = numAttempts;
287 }
288 };
289
290 /**
291 * Default garage mode policy.
292 *
293 * The first wake up time is set to be 1am the next day. And it keeps waking up every day for a
294 * week. After that, wake up every 7 days for a month, and wake up every 30 days thereafter.
295 */
296 @VisibleForTesting
297 static final class GarageModePolicy {
298 private static final Map<String, Integer> TIME_UNITS_LOOKUP;
299 static {
300 TIME_UNITS_LOOKUP = new HashMap<String, Integer>();
301 TIME_UNITS_LOOKUP.put("m", 60);
302 TIME_UNITS_LOOKUP.put("h", 3600);
303 TIME_UNITS_LOOKUP.put("d", 86400);
304 }
305 protected WakeupTime[] mWakeupTime;
306
307 public GarageModePolicy(String[] policy) {
308 if (policy == null || policy.length == 0) {
309 throw new RuntimeException("Must include valid policy.");
310 }
311
312 WakeupTime[] parsedWakeupTime = new WakeupTime[policy.length];
313 for (int i = 0; i < policy.length; i++) {
314 String[] splitString = policy[i].split(",");
315 if (splitString.length != 2) {
316 throw new RuntimeException("Bad policy format: " + policy[i]);
317 }
318
319 int wakeupTimeMs = 0;
320 int numAttempts = 0;
321 String wakeupTime = splitString[0];
322 if (wakeupTime.isEmpty()) {
323 throw new RuntimeException("Missing wakeup time: " + policy[i]);
324 }
325 String wakeupTimeValue = wakeupTime.substring(0, wakeupTime.length() - 1);
326
327 if (wakeupTimeValue.isEmpty()) {
328 throw new RuntimeException("Missing wakeup time value: " + wakeupTime);
329 }
330 try {
331 int timeCount = Integer.parseInt(wakeupTimeValue);
332 if (timeCount <= 0) {
333 throw new RuntimeException("Wakeup time must be > 0: " + timeCount);
334 }
335
336 // Last character indicates minutes, hours, or days.
337 Integer multiplier = TIME_UNITS_LOOKUP.get(
338 wakeupTime.substring(wakeupTime.length() - 1));
339 if (multiplier == null) {
340 throw new RuntimeException("Bad wakeup time units: " + wakeupTime);
341 }
342
343 wakeupTimeMs = timeCount * multiplier;
344 } catch (NumberFormatException e) {
345 throw new RuntimeException("Bad wakeup time value: " + wakeupTimeValue);
346 }
347 try {
348 numAttempts = Integer.parseInt(splitString[1]);
349 if (numAttempts <= 0) {
350 throw new RuntimeException(
351 "Number of attempts must be > 0: " + numAttempts);
352 }
353 } catch (NumberFormatException e) {
354 throw new RuntimeException("Bad number of attempts: " + splitString[1]);
355 }
356
357 parsedWakeupTime[i] = new WakeupTime(wakeupTimeMs, numAttempts);
358 }
359 mWakeupTime = parsedWakeupTime;
360 }
361
362 public int getNextWakeUpTime(int index) {
363 if (mWakeupTime == null) {
364 Log.e(TAG, "Could not parse policy.");
365 return 0;
366 }
367
368 for (int i = 0; i < mWakeupTime.length; i++) {
369 if (index < mWakeupTime[i].mNumAttempts) {
370 return mWakeupTime[i].mWakeupTime;
371 } else {
372 index -= mWakeupTime[i].mNumAttempts;
373 }
374 }
375
376 Log.w(TAG, "No more garage mode wake ups scheduled; been sleeping too long.");
377 return 0;
378 }
379 }
380 276
381 private static class DefaultDeviceIdleController extends DeviceIdleControllerWrapper { 277 private static class DefaultDeviceIdleController extends DeviceIdleControllerWrapper {
382 private IDeviceIdleController mDeviceIdleController; 278 private IDeviceIdleController mDeviceIdleController;
383 private MaintenanceActivityListener mMaintenanceActivityListener 279 private MaintenanceActivityListener mMaintenanceActivityListener =
384 = new MaintenanceActivityListener(); 280 new MaintenanceActivityListener();
385 281
386 @Override 282 @Override
387 public boolean startLocked() { 283 public boolean startLocked() {
@@ -417,12 +313,6 @@ public class GarageModeService implements CarServiceBase,
417 } 313 }
418 } 314 }
419 315
420 private void logd(String msg) {
421 if (DBG) {
422 Log.d(TAG, msg);
423 }
424 }
425
426 @GuardedBy("this") 316 @GuardedBy("this")
427 private void readFromSettingsLocked(String... keys) { 317 private void readFromSettingsLocked(String... keys) {
428 for (String key : keys) { 318 for (String key : keys) {
@@ -444,7 +334,7 @@ public class GarageModeService implements CarServiceBase,
444 334
445 private void onSettingsChangedInternal(Uri uri) { 335 private void onSettingsChangedInternal(Uri uri) {
446 synchronized (this) { 336 synchronized (this) {
447 logd("Content Observer onChange: " + uri); 337 Log.d(TAG, "Content Observer onChange: " + uri);
448 if (uri.equals(GARAGE_MODE_ENABLED_URI)) { 338 if (uri.equals(GARAGE_MODE_ENABLED_URI)) {
449 readFromSettingsLocked(CarSettings.Global.KEY_GARAGE_MODE_ENABLED); 339 readFromSettingsLocked(CarSettings.Global.KEY_GARAGE_MODE_ENABLED);
450 } else if (uri.equals(GARAGE_MODE_WAKE_UP_TIME_URI)) { 340 } else if (uri.equals(GARAGE_MODE_WAKE_UP_TIME_URI)) {
@@ -452,7 +342,7 @@ public class GarageModeService implements CarServiceBase,
452 } else if (uri.equals(GARAGE_MODE_MAINTENANCE_WINDOW_URI)) { 342 } else if (uri.equals(GARAGE_MODE_MAINTENANCE_WINDOW_URI)) {
453 readFromSettingsLocked(CarSettings.Global.KEY_GARAGE_MODE_MAINTENANCE_WINDOW); 343 readFromSettingsLocked(CarSettings.Global.KEY_GARAGE_MODE_MAINTENANCE_WINDOW);
454 } 344 }
455 logd(String.format( 345 Log.d(TAG, String.format(
456 "onSettingsChanged %s. enabled: %s, windowSize: %d", 346 "onSettingsChanged %s. enabled: %s, windowSize: %d",
457 uri, mGarageModeEnabled, mMaintenanceWindow)); 347 uri, mGarageModeEnabled, mMaintenanceWindow));
458 } 348 }
diff --git a/service/src/com/android/car/garagemode/WakeupInterval.java b/service/src/com/android/car/garagemode/WakeupInterval.java
new file mode 100644
index 00000000..659af9ea
--- /dev/null
+++ b/service/src/com/android/car/garagemode/WakeupInterval.java
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2015 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.garagemode;
18
19/**
20 * Defines wake up interval which then will be used by
21 * {@link com.android.car.garagemode.GarageModeService} to determine when to schedule next wake up
22 * from {@link com.android.car.CarPowerManagementService}
23 */
24class WakeupInterval {
25 private int mWakeupInterval;
26 private int mNumAttempts;
27
28 WakeupInterval(int wakeupTime, int numAttempts) {
29 mWakeupInterval = wakeupTime;
30 mNumAttempts = numAttempts;
31 }
32
33 /**
34 * Returns interval between now and next weke up.
35 * @return interval in seconds
36 */
37 public int getWakeupInterval() {
38 return mWakeupInterval;
39 }
40
41 /**
42 * Returns amount of attempts to wake up with mWakeupInterval
43 * @return amount of attempts
44 */
45 public int getNumAttempts() {
46 return mNumAttempts;
47 }
48}
diff --git a/service/src/com/android/car/hal/CarPropertyUtils.java b/service/src/com/android/car/hal/CarPropertyUtils.java
index d9151e20..2d0e6ad6 100644
--- a/service/src/com/android/car/hal/CarPropertyUtils.java
+++ b/service/src/com/android/car/hal/CarPropertyUtils.java
@@ -48,27 +48,43 @@ import java.util.List;
48 long timestamp = halValue.timestamp; 48 long timestamp = halValue.timestamp;
49 VehiclePropValue.RawValue v = halValue.value; 49 VehiclePropValue.RawValue v = halValue.value;
50 50
51 // Handles each return value from {@link getJavaClass}.
51 if (Boolean.class == clazz) { 52 if (Boolean.class == clazz) {
52 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, 53 return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
53 v.int32Values.get(0) == 1); 54 v.int32Values.get(0) == 1);
54 } else if (Boolean[].class == clazz) { 55 } else if (Float.class == clazz) {
55 Boolean[] values = new Boolean[v.int32Values.size()]; 56 return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
57 v.floatValues.get(0));
58 } else if (Integer.class == clazz) {
59 return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
60 v.int32Values.get(0));
61 } else if (Long.class == clazz) {
62 return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
63 v.int64Values.get(0));
64 } else if (Float[].class == clazz) {
65 Float[] values = new Float[v.floatValues.size()];
56 for (int i = 0; i < values.length; i++) { 66 for (int i = 0; i < values.length; i++) {
57 values[i] = v.int32Values.get(i) == 1; 67 values[i] = v.floatValues.get(i);
68 }
69 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
70 } else if (Integer[].class == clazz) {
71 Integer[] values = new Integer[v.int32Values.size()];
72 for (int i = 0; i < values.length; i++) {
73 values[i] = v.int32Values.get(i);
58 } 74 }
59 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values); 75 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
60 } else if (String.class == clazz) {
61 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, v.stringValue);
62 } else if (byte[].class == clazz) {
63 byte[] halData = toByteArray(v.bytes);
64 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, halData);
65 } else if (Long[].class == clazz) { 76 } else if (Long[].class == clazz) {
66 Long[] values = new Long[v.int64Values.size()]; 77 Long[] values = new Long[v.int64Values.size()];
67 for (int i = 0; i < values.length; i++) { 78 for (int i = 0; i < values.length; i++) {
68 values[i] = v.int64Values.get(i); 79 values[i] = v.int64Values.get(i);
69 } 80 }
70 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values); 81 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
71 } else /* All list properties */ { 82 } else if (String.class == clazz) {
83 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, v.stringValue);
84 } else if (byte[].class == clazz) {
85 byte[] halData = toByteArray(v.bytes);
86 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, halData);
87 } else /* Object.class */ {
72 Object[] values = getRawValueList(clazz, v).toArray(); 88 Object[] values = getRawValueList(clazz, v).toArray();
73 return new CarPropertyValue<>(propertyId, areaId, status, timestamp, 89 return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
74 values.length == 1 ? values[0] : values); 90 values.length == 1 ? values[0] : values);
@@ -88,7 +104,7 @@ import java.util.List;
88 v.int32Values.add(((Boolean) o) ? 1 : 0); 104 v.int32Values.add(((Boolean) o) ? 1 : 0);
89 } else if (o instanceof Boolean[]) { 105 } else if (o instanceof Boolean[]) {
90 for (Boolean b : (Boolean[]) o) { 106 for (Boolean b : (Boolean[]) o) {
91 v.int32Values.add(((Boolean) o) ? 1 : 0); 107 v.int32Values.add(b ? 1 : 0);
92 } 108 }
93 } else if (o instanceof Integer) { 109 } else if (o instanceof Integer) {
94 v.int32Values.add((Integer) o); 110 v.int32Values.add((Integer) o);
diff --git a/service/src/com/android/car/hal/PropertyHalServiceIds.java b/service/src/com/android/car/hal/PropertyHalServiceIds.java
index 651afea2..6c5533de 100644
--- a/service/src/com/android/car/hal/PropertyHalServiceIds.java
+++ b/service/src/com/android/car/hal/PropertyHalServiceIds.java
@@ -215,9 +215,6 @@ public class PropertyHalServiceIds {
215 mProps.put(VehicleProperty.HVAC_SEAT_VENTILATION, new Pair<>( 215 mProps.put(VehicleProperty.HVAC_SEAT_VENTILATION, new Pair<>(
216 Car.PERMISSION_CONTROL_CAR_CLIMATE, 216 Car.PERMISSION_CONTROL_CAR_CLIMATE,
217 Car.PERMISSION_CONTROL_CAR_CLIMATE)); 217 Car.PERMISSION_CONTROL_CAR_CLIMATE));
218 mProps.put(VehicleProperty.ENV_OUTSIDE_TEMPERATURE, new Pair<>(
219 Car.PERMISSION_CONTROL_CAR_CLIMATE,
220 Car.PERMISSION_CONTROL_CAR_CLIMATE));
221 218
222 // Info properties 219 // Info properties
223 mProps.put(VehicleProperty.INFO_VIN, new Pair<>( 220 mProps.put(VehicleProperty.INFO_VIN, new Pair<>(
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index 930a9e43..ed51a782 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -40,11 +40,13 @@ import android.content.pm.ServiceInfo;
40import android.content.pm.Signature; 40import android.content.pm.Signature;
41import android.content.res.Resources; 41import android.content.res.Resources;
42import android.os.Binder; 42import android.os.Binder;
43import android.os.Build;
43import android.os.Handler; 44import android.os.Handler;
44import android.os.HandlerThread; 45import android.os.HandlerThread;
45import android.os.Looper; 46import android.os.Looper;
46import android.os.Message; 47import android.os.Message;
47import android.os.Process; 48import android.os.Process;
49import android.text.TextUtils;
48import android.text.format.DateFormat; 50import android.text.format.DateFormat;
49import android.util.ArraySet; 51import android.util.ArraySet;
50import android.util.Log; 52import android.util.Log;
@@ -92,6 +94,8 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
92 // Store the white list and black list strings from the resource file. 94 // Store the white list and black list strings from the resource file.
93 private String mConfiguredWhitelist; 95 private String mConfiguredWhitelist;
94 private String mConfiguredBlacklist; 96 private String mConfiguredBlacklist;
97 private final List<String> mAllowedAppInstallSources;
98
95 /** 99 /**
96 * Hold policy set from policy service or client. 100 * Hold policy set from policy service or client.
97 * Key: packageName of policy service 101 * Key: packageName of policy service
@@ -152,8 +156,11 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
152 mEnableActivityBlocking = res.getBoolean(R.bool.enableActivityBlockingForSafety); 156 mEnableActivityBlocking = res.getBoolean(R.bool.enableActivityBlockingForSafety);
153 String blockingActivity = res.getString(R.string.activityBlockingActivity); 157 String blockingActivity = res.getString(R.string.activityBlockingActivity);
154 mActivityBlockingActivity = ComponentName.unflattenFromString(blockingActivity); 158 mActivityBlockingActivity = ComponentName.unflattenFromString(blockingActivity);
159 mAllowedAppInstallSources = Arrays.asList(
160 res.getStringArray(R.array.allowedAppInstallSources));
155 } 161 }
156 162
163
157 @Override 164 @Override
158 public void setAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags) { 165 public void setAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags) {
159 if (DBG_POLICY_SET) { 166 if (DBG_POLICY_SET) {
@@ -535,8 +542,10 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
535 // Add the blocking overlay activity to the whitelist, since that needs to run in a 542 // Add the blocking overlay activity to the whitelist, since that needs to run in a
536 // restricted state to communicate the reason an app was blocked. 543 // restricted state to communicate the reason an app was blocked.
537 Set<String> defaultActivity = new ArraySet<>(); 544 Set<String> defaultActivity = new ArraySet<>();
538 defaultActivity.add(mActivityBlockingActivity.getClassName()); 545 if (mActivityBlockingActivity != null) {
539 configWhitelist.put(mActivityBlockingActivity.getPackageName(), defaultActivity); 546 defaultActivity.add(mActivityBlockingActivity.getClassName());
547 configWhitelist.put(mActivityBlockingActivity.getPackageName(), defaultActivity);
548 }
540 549
541 List<PackageInfo> packages = mPackageManager.getInstalledPackages( 550 List<PackageInfo> packages = mPackageManager.getInstalledPackages(
542 PackageManager.GET_SIGNATURES | PackageManager.GET_ACTIVITIES 551 PackageManager.GET_SIGNATURES | PackageManager.GET_ACTIVITIES
@@ -583,7 +592,31 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
583 } 592 }
584 } else { 593 } else {
585 /* 2. If app is not listed in the config.xml check their Manifest meta-data to 594 /* 2. If app is not listed in the config.xml check their Manifest meta-data to
586 see if they have any Distraction Optimized(DO) activities */ 595 see if they have any Distraction Optimized(DO) activities.
596 For non system apps, we check if the app install source was a permittable
597 source. This prevents side-loaded apps to fake DO. Bypass the check
598 for debug builds for development convenience. */
599 if (!isDebugBuild()
600 && !info.applicationInfo.isSystemApp()
601 && !info.applicationInfo.isUpdatedSystemApp()) {
602 try {
603 if (mAllowedAppInstallSources != null) {
604 String installerName = mPackageManager.getInstallerPackageName(
605 info.packageName);
606 if (installerName == null || (installerName != null
607 && !mAllowedAppInstallSources.contains(installerName))) {
608 Log.w(CarLog.TAG_PACKAGE,
609 info.packageName + " not installed from permitted sources "
610 + installerName == null ? "NULL" : installerName);
611 continue;
612 }
613 }
614 } catch (IllegalArgumentException e) {
615 Log.w(CarLog.TAG_PACKAGE, info.packageName + " not installed!");
616 continue;
617 }
618 }
619
587 try { 620 try {
588 activities = CarAppMetadataReader.findDistractionOptimizedActivities( 621 activities = CarAppMetadataReader.findDistractionOptimizedActivities(
589 mContext, 622 mContext,
@@ -622,6 +655,10 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
622 } 655 }
623 } 656 }
624 657
658 private boolean isDebugBuild() {
659 return Build.IS_USERDEBUG || Build.IS_ENG;
660 }
661
625 /** 662 /**
626 * Generate a map of blacklisted packages and activities of the form {pkgName, Blacklisted 663 * Generate a map of blacklisted packages and activities of the form {pkgName, Blacklisted
627 * activities}. The blacklist information comes from a configuration XML resource. 664 * activities}. The blacklist information comes from a configuration XML resource.
@@ -644,6 +681,11 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
644 } 681 }
645 682
646 for (String pkg : configBlacklist.keySet()) { 683 for (String pkg : configBlacklist.keySet()) {
684 if (TextUtils.isEmpty(pkg)) {
685 // This means there is nothing to blacklist
686 Log.d(CarLog.TAG_PACKAGE, "Empty string in blacklist pkg");
687 continue;
688 }
647 int flags = 0; 689 int flags = 0;
648 PackageInfo pkgInfo; 690 PackageInfo pkgInfo;
649 String[] activities; 691 String[] activities;
@@ -654,7 +696,7 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
654 | PackageManager.MATCH_DIRECT_BOOT_AWARE 696 | PackageManager.MATCH_DIRECT_BOOT_AWARE
655 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 697 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
656 } catch (NameNotFoundException e) { 698 } catch (NameNotFoundException e) {
657 Log.e(CarLog.TAG_PACKAGE, pkg + " not found to blacklist " + e); 699 Log.e(CarLog.TAG_PACKAGE, pkg + " not found to blacklist ", e);
658 continue; 700 continue;
659 } 701 }
660 702
@@ -943,8 +985,18 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
943 } 985 }
944 } 986 }
945 987
988 /**
989 * Enable/Disable activity blocking by correspondingly enabling/disabling broadcasting UXR
990 * changes in {@link CarUxRestrictionsManagerService}. This is only available in
991 * engineering builds for development convenience.
992 *
993 */
946 @Override 994 @Override
947 public synchronized void setEnableActivityBlocking(boolean enable) { 995 public synchronized void setEnableActivityBlocking(boolean enable) {
996 if (!isDebugBuild()) {
997 Log.e(CarLog.TAG_PACKAGE, "Cannot enable/disable activity blocking");
998 return;
999 }
948 // Check if the caller has the same signature as that of the car service. 1000 // Check if the caller has the same signature as that of the car service.
949 if (mPackageManager.checkSignatures(Process.myUid(), Binder.getCallingUid()) 1001 if (mPackageManager.checkSignatures(Process.myUid(), Binder.getCallingUid())
950 != PackageManager.SIGNATURE_MATCH) { 1002 != PackageManager.SIGNATURE_MATCH) {
@@ -952,7 +1004,7 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
952 "Caller " + mPackageManager.getNameForUid(Binder.getCallingUid()) 1004 "Caller " + mPackageManager.getNameForUid(Binder.getCallingUid())
953 + " does not have the right signature"); 1005 + " does not have the right signature");
954 } 1006 }
955 mEnableActivityBlocking = enable; 1007 mCarUxRestrictionsService.setUxRChangeBroadcastEnabled(enable);
956 } 1008 }
957 1009
958 /** 1010 /**
diff --git a/service/src/com/android/car/systeminterface/DisplayInterface.java b/service/src/com/android/car/systeminterface/DisplayInterface.java
index 015f54dc..8dc7bd40 100644
--- a/service/src/com/android/car/systeminterface/DisplayInterface.java
+++ b/service/src/com/android/car/systeminterface/DisplayInterface.java
@@ -16,6 +16,10 @@
16 16
17package com.android.car.systeminterface; 17package com.android.car.systeminterface;
18 18
19import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
20import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear;
21import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma;
22
19import android.content.ContentResolver; 23import android.content.ContentResolver;
20import android.content.Context; 24import android.content.Context;
21import android.database.ContentObserver; 25import android.database.ContentObserver;
@@ -60,20 +64,18 @@ public interface DisplayInterface {
60 new ContentObserver(new Handler(Looper.getMainLooper())) { 64 new ContentObserver(new Handler(Looper.getMainLooper())) {
61 @Override 65 @Override
62 public void onChange(boolean selfChange) { 66 public void onChange(boolean selfChange) {
63 int brightness = mMinimumBacklight; 67 int linear = GAMMA_SPACE_MAX;
64 int range = mMaximumBacklight - mMinimumBacklight;
65 68
66 try { 69 try {
67 brightness = System.getInt(mContentResolver, System.SCREEN_BRIGHTNESS); 70 linear = System.getInt(mContentResolver, System.SCREEN_BRIGHTNESS);
68 } catch (SettingNotFoundException e) { 71 } catch (SettingNotFoundException e) {
69 Log.e(CarLog.TAG_POWER, "Could not get SCREEN_BRIGHTNESS: " + e); 72 Log.e(CarLog.TAG_POWER, "Could not get SCREEN_BRIGHTNESS: " + e);
70 } 73 }
71 // Convert brightness from 0-255 to 0-100% 74 int gamma = convertLinearToGamma(linear, mMinimumBacklight,
72 brightness -= mMinimumBacklight; 75 mMaximumBacklight);
73 brightness *= 100; 76 int percentBright = (gamma * 100 + ((GAMMA_SPACE_MAX + 1) / 2))
74 brightness += (range + 1) / 2; 77 / GAMMA_SPACE_MAX;
75 brightness /= range; 78 mService.sendDisplayBrightness(percentBright);
76 mService.sendDisplayBrightness(brightness);
77 } 79 }
78 }; 80 };
79 81
@@ -124,26 +126,10 @@ public interface DisplayInterface {
124 } 126 }
125 127
126 @Override 128 @Override
127 public void setDisplayBrightness(int brightness) { 129 public void setDisplayBrightness(int percentBright) {
128 // Brightness is set in percent. Need to convert this into 0-255 scale. The actual 130 int gamma = (percentBright * GAMMA_SPACE_MAX + 50) / 100;
129 // brightness algorithm should look like this: 131 int linear = convertGammaToLinear(gamma, mMinimumBacklight, mMaximumBacklight);
130 // 132 System.putInt(mContentResolver, System.SCREEN_BRIGHTNESS, linear);
131 // newBrightness = (brightness * (max - min)) + min
132 //
133 // Since we're using integer arithmetic, do the multiplication first, then add 50 to
134 // round up as needed.
135 brightness *= mMaximumBacklight - mMinimumBacklight; // Multiply by full range
136 brightness += 50; // Integer rounding
137 brightness /= 100; // Divide by 100
138 brightness += mMinimumBacklight;
139 // Range checking
140 if (brightness < mMinimumBacklight) {
141 brightness = mMinimumBacklight;
142 } else if (brightness > mMaximumBacklight) {
143 brightness = mMaximumBacklight;
144 }
145 // Set the brightness
146 System.putInt(mContentResolver, System.SCREEN_BRIGHTNESS, brightness);
147 } 133 }
148 134
149 @Override 135 @Override
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 74ff78d4..82e981f0 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -17,13 +17,15 @@
17package com.android.car.user; 17package com.android.car.user;
18 18
19import android.annotation.Nullable; 19import android.annotation.Nullable;
20import android.car.settings.CarSettings;
21import android.car.user.CarUserManagerHelper; 20import android.car.user.CarUserManagerHelper;
22import android.content.BroadcastReceiver; 21import android.content.BroadcastReceiver;
23import android.content.Context; 22import android.content.Context;
24import android.content.Intent; 23import android.content.Intent;
25import android.content.IntentFilter; 24import android.content.IntentFilter;
26import android.content.pm.UserInfo; 25import android.content.pm.UserInfo;
26import android.location.LocationManager;
27import android.os.UserHandle;
28import android.os.UserManager;
27import android.util.Log; 29import android.util.Log;
28 30
29import com.android.car.CarServiceBase; 31import com.android.car.CarServiceBase;
@@ -36,13 +38,13 @@ import java.io.PrintWriter;
36 * 38 *
37 * <ol> 39 * <ol>
38 * <li> Creates a secondary admin user on first run. 40 * <li> Creates a secondary admin user on first run.
39 * <li> Log in to a default user. 41 * <li> Log in to the last active user.
40 * <ol/> 42 * <ol/>
41 */ 43 */
42public class CarUserService extends BroadcastReceiver implements CarServiceBase { 44public class CarUserService extends BroadcastReceiver implements CarServiceBase {
43 // Place holder for user name of the first user created. 45 // Place holder for user name of the first user created.
44 @VisibleForTesting 46 @VisibleForTesting
45 static final String OWNER_NAME = "Owner"; 47 static final String OWNER_NAME = "Driver";
46 private static final String TAG = "CarUserService"; 48 private static final String TAG = "CarUserService";
47 private final Context mContext; 49 private final Context mContext;
48 private final CarUserManagerHelper mCarUserManagerHelper; 50 private final CarUserManagerHelper mCarUserManagerHelper;
@@ -63,6 +65,7 @@ public class CarUserService extends BroadcastReceiver implements CarServiceBase
63 } 65 }
64 IntentFilter filter = new IntentFilter(); 66 IntentFilter filter = new IntentFilter();
65 filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); 67 filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
68 filter.addAction(Intent.ACTION_USER_SWITCHED);
66 69
67 mContext.registerReceiver(this, filter); 70 mContext.registerReceiver(this, filter);
68 } 71 }
@@ -87,13 +90,38 @@ public class CarUserService extends BroadcastReceiver implements CarServiceBase
87 Log.d(TAG, "onReceive " + intent); 90 Log.d(TAG, "onReceive " + intent);
88 } 91 }
89 92
90 if (intent.getAction() == Intent.ACTION_LOCKED_BOOT_COMPLETED) { 93 if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) {
91 if (mCarUserManagerHelper.getAllUsers().size() == 0) { 94 if (mCarUserManagerHelper.getAllUsers().size() == 0) {
95 setSystemUserRestrictions();
96 mCarUserManagerHelper.initDefaultGuestRestrictions();
97 // On very first boot, create an admin user and switch to that user.
92 UserInfo admin = mCarUserManagerHelper.createNewAdminUser(OWNER_NAME); 98 UserInfo admin = mCarUserManagerHelper.createNewAdminUser(OWNER_NAME);
93 mCarUserManagerHelper.switchToUser(admin); 99 mCarUserManagerHelper.switchToUser(admin);
100 mCarUserManagerHelper.setLastActiveUser(
101 admin.id, /* skipGlobalSettings= */ false);
94 } else { 102 } else {
95 mCarUserManagerHelper.switchToUserId(CarSettings.DEFAULT_USER_ID_TO_BOOT_INTO); 103 mCarUserManagerHelper.switchToUserId(mCarUserManagerHelper.getInitialUser());
104 }
105 } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
106 // Update last active user if the switched-to user is a persistent, non-system user.
107 int currentUser = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
108 if (currentUser > UserHandle.USER_SYSTEM
109 && mCarUserManagerHelper.isPersistentUser(currentUser)) {
110 mCarUserManagerHelper.setLastActiveUser(
111 currentUser, /* skipGlobalSetting= */ false);
96 } 112 }
97 } 113 }
98 } 114 }
115
116 private void setSystemUserRestrictions() {
117 // Disable adding accounts for system user.
118 mCarUserManagerHelper.setUserRestriction(mCarUserManagerHelper.getSystemUserInfo(),
119 UserManager.DISALLOW_MODIFY_ACCOUNTS, /* enable= */ true);
120
121 // Disable Location service for system user.
122 LocationManager locationManager =
123 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
124 locationManager.setLocationEnabledForUser(
125 /* enabled= */ false, UserHandle.of(UserHandle.USER_SYSTEM));
126 }
99} 127}
diff --git a/tests/CarTrustAgentClientApp/Android.mk b/tests/CarTrustAgentClientApp/Android.mk
new file mode 100644
index 00000000..7945ee5f
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/Android.mk
@@ -0,0 +1,15 @@
1LOCAL_PATH:= $(call my-dir)
2include $(CLEAR_VARS)
3
4LOCAL_PACKAGE_NAME := CarTrustAgentClient
5
6LOCAL_USE_AAPT2 := true
7LOCAL_SRC_FILES := $(call all-java-files-under, src)
8LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
9
10LOCAL_CERTIFICATE := platform
11LOCAL_MODULE_TAGS := optional
12LOCAL_MIN_SDK_VERSION := 23
13LOCAL_SDK_VERSION := current
14
15include $(BUILD_PACKAGE)
diff --git a/tests/CarTrustAgentClientApp/AndroidManifest.xml b/tests/CarTrustAgentClientApp/AndroidManifest.xml
new file mode 100644
index 00000000..35a9a6db
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/AndroidManifest.xml
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="utf-8"?>
2<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="com.android.car.trust.client">
4
5 <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23"/>
6
7 <!-- Need Bluetooth LE -->
8 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
9
10 <uses-permission android:name="android.permission.BLUETOOTH" />
11 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
12 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
13 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
14
15 <!-- Needed to unlock user -->
16 <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
17 <uses-permission android:name="android.permission.MANAGE_USERS" />
18 <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
19 <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
20 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
21
22 <application android:label="@string/app_name">
23 <activity
24 android:name=".PhoneEnrolmentActivity"
25 android:label="@string/app_name"
26 android:exported="true"
27 android:launchMode="singleInstance">
28 <intent-filter>
29 <action android:name="android.intent.action.MAIN" />
30 <category android:name="android.intent.category.LAUNCHER" />
31 </intent-filter>
32 </activity>
33 </application>
34</manifest>
diff --git a/tests/CarTrustAgentClientApp/README.txt b/tests/CarTrustAgentClientApp/README.txt
new file mode 100644
index 00000000..bf6c4444
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/README.txt
@@ -0,0 +1,2 @@
1IMPORTANT NOTE: This is a reference app to smart unlock paired HU during development.
2Consider moving the functionality to a more proper place.
diff --git a/tests/CarTrustAgentClientApp/res/layout/phone_enrolment_activity.xml b/tests/CarTrustAgentClientApp/res/layout/phone_enrolment_activity.xml
new file mode 100644
index 00000000..620e04e8
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/res/layout/phone_enrolment_activity.xml
@@ -0,0 +1,54 @@
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 android:weightSum="1">
7 <ScrollView
8 android:id="@+id/scroll"
9 android:layout_width="match_parent"
10 android:layout_height="0dp"
11 android:scrollbars="vertical"
12 android:layout_weight="0.80">
13 <TextView
14 android:layout_width="match_parent"
15 android:layout_height="wrap_content"
16 android:id="@+id/output"/>
17 </ScrollView>
18 <LinearLayout
19 android:layout_width="match_parent"
20 android:layout_height="0dp"
21 android:layout_weight="0.10"
22 android:orientation="horizontal">
23 <Button
24 android:id="@+id/enroll_scan"
25 android:layout_width="0dp"
26 android:layout_height="match_parent"
27 android:layout_weight="2"
28 android:text="@string/enroll_scan"/>
29 <Button
30 android:id="@+id/enroll_button"
31 android:layout_width="0dp"
32 android:layout_height="match_parent"
33 android:layout_weight="3"
34 android:text="@string/enroll_button"/>
35 </LinearLayout>
36 <LinearLayout
37 android:layout_width="match_parent"
38 android:layout_height="0dp"
39 android:layout_weight="0.10"
40 android:orientation="horizontal">
41 <Button
42 android:id="@+id/unlock_scan"
43 android:layout_width="0dp"
44 android:layout_height="match_parent"
45 android:layout_weight="2"
46 android:text="@string/unlock_scan"/>
47 <Button
48 android:id="@+id/unlock_button"
49 android:layout_width="0dp"
50 android:layout_height="match_parent"
51 android:layout_weight="3"
52 android:text="@string/unlock_button"/>
53 </LinearLayout>
54</LinearLayout>
diff --git a/tests/CarTrustAgentClientApp/res/values/strings.xml b/tests/CarTrustAgentClientApp/res/values/strings.xml
new file mode 100644
index 00000000..5c9b4db7
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/res/values/strings.xml
@@ -0,0 +1,22 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
3 <string name="app_name">CarTrustAgentClient</string>
4
5 <!-- service/characteristics uuid for unlocking a device -->
6 <string name="unlock_service_uuid">5e2a68a1-27be-43f9-8d1e-4546976fabd7</string>
7 <string name="unlock_escrow_token_uiid">5e2a68a2-27be-43f9-8d1e-4546976fabd7</string>
8 <string name="unlock_handle_uiid">5e2a68a3-27be-43f9-8d1e-4546976fabd7</string>
9
10 <!-- service/characteristics uuid for adding new escrow token -->
11 <string name="enrollment_service_uuid">5e2a68a4-27be-43f9-8d1e-4546976fabd7</string>
12 <string name="enrollment_handle_uuid">5e2a68a5-27be-43f9-8d1e-4546976fabd7</string>
13 <string name="enrollment_token_uuid">5e2a68a6-27be-43f9-8d1e-4546976fabd7</string>
14
15 <string name="pref_key_token_handle">token-handle-key</string>
16 <string name="pref_key_escrow_token">escrow-token-key</string>
17
18 <string name="enroll_button">Enroll new token</string>
19 <string name="enroll_scan">Scan to enroll</string>
20 <string name="unlock_button">Unlock</string>
21 <string name="unlock_scan">Scan to unlock</string>
22</resources>
diff --git a/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentActivity.java b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentActivity.java
new file mode 100644
index 00000000..c1d30c18
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentActivity.java
@@ -0,0 +1,61 @@
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 */
16package com.android.car.trust.client;
17
18import android.Manifest;
19import android.app.Activity;
20import android.content.pm.PackageManager;
21import android.os.Bundle;
22
23/**
24 * Activity to allow the user to add an escrow token to a remote device. <p/>
25 *
26 * For this to work properly, the correct permissions must be set in the system config. In AOSP,
27 * this config is in frameworks/base/core/res/res/values/config.xml <p/>
28 *
29 * The config must set config_allowEscrowTokenForTrustAgent to true. For the desired car
30 * experience, the config should also set config_strongAuthRequiredOnBoot to false.
31 */
32public class PhoneEnrolmentActivity extends Activity {
33
34 private static final int FINE_LOCATION_REQUEST_CODE = 42;
35
36 @Override
37 protected void onCreate(Bundle savedInstanceState) {
38 super.onCreate(savedInstanceState);
39 setContentView(R.layout.phone_enrolment_activity);
40
41 PhoneEnrolmentController enrolmentController = new PhoneEnrolmentController(this);
42 enrolmentController.bind(findViewById(R.id.output), findViewById(R.id.enroll_scan),
43 findViewById(R.id.enroll_button));
44
45 PhoneUnlockController unlockController = new PhoneUnlockController(this);
46 unlockController.bind(findViewById(R.id.output), findViewById(R.id.unlock_scan),
47 findViewById(R.id.unlock_button));
48 }
49
50 @Override
51 protected void onResume() {
52 super.onResume();
53
54 if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
55 != PackageManager.PERMISSION_GRANTED) {
56 requestPermissions(
57 new String[] { android.Manifest.permission.ACCESS_FINE_LOCATION },
58 FINE_LOCATION_REQUEST_CODE);
59 }
60 }
61}
diff --git a/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentController.java b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentController.java
new file mode 100644
index 00000000..030e3d2a
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneEnrolmentController.java
@@ -0,0 +1,183 @@
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 */
16package com.android.car.trust.client;
17
18import android.bluetooth.BluetoothDevice;
19import android.bluetooth.BluetoothGatt;
20import android.bluetooth.BluetoothGattCharacteristic;
21import android.bluetooth.BluetoothGattService;
22import android.content.Context;
23import android.content.SharedPreferences;
24import android.os.Handler;
25import android.os.ParcelUuid;
26import android.preference.PreferenceManager;
27import android.util.Base64;
28import android.util.Log;
29import android.widget.Button;
30import android.widget.TextView;
31
32import java.nio.ByteBuffer;
33import java.util.Random;
34import java.util.UUID;
35
36/**
37 * A controller that sets up a {@link SimpleBleClient} to connect to the BLE enrollment service.
38 * It also binds the UI components to control the enrollment process.
39 */
40public class PhoneEnrolmentController {
41
42 private final SimpleBleClient.ClientCallback mCallback = new SimpleBleClient.ClientCallback() {
43 @Override
44 public void onDeviceConnected(BluetoothDevice device) {
45 appendOutputText("Device connected: " + device.getName()
46 + " addr: " + device.getAddress());
47 }
48
49 @Override
50 public void onDeviceDisconnected() {
51 appendOutputText("Device disconnected");
52 }
53
54 @Override
55 public void onCharacteristicChanged(BluetoothGatt gatt,
56 BluetoothGattCharacteristic characteristic) {
57
58 Log.d(Utils.LOG_TAG, "onCharacteristicChanged: "
59 + Utils.getLong(characteristic.getValue()));
60 if (characteristic.getUuid().equals(mEnrolmentTokenHandle.getUuid())) {
61 // Store the new token handle that the BLE server is sending us. This required
62 // to unlock the device.
63 long handle = Utils.getLong(characteristic.getValue());
64 storeHandle(handle);
65 appendOutputText("Token handle received: " + handle);
66 }
67 }
68
69 @Override
70 public void onServiceDiscovered(BluetoothGattService service) {
71 if (!service.getUuid().equals(mEnrolmentServiceUuid.getUuid())) {
72 Log.d(Utils.LOG_TAG, "Service UUID: " + service.getUuid()
73 + " does not match Enrolment UUID " + mEnrolmentServiceUuid.getUuid());
74 return;
75 }
76
77 Log.d(Utils.LOG_TAG, "Enrolment Service # characteristics: "
78 + service.getCharacteristics().size());
79 mEnrolmentEscrowToken = Utils.getCharacteristic(
80 R.string.enrollment_token_uuid, service, mContext);
81 mEnrolmentTokenHandle = Utils.getCharacteristic(
82 R.string.enrollment_handle_uuid, service, mContext);
83 mClient.setCharacteristicNotification(mEnrolmentTokenHandle, true /* enable */);
84 appendOutputText("Enrolment BLE client successfully connected");
85
86 mHandler.post(() -> {
87 // Services are now set up, allow users to enrol new escrow tokens.
88 mEnrolButton.setEnabled(true);
89 mEnrolButton.setAlpha(1.0f);
90 });
91 }
92 };
93
94 private String mTokenHandleKey;
95 private String mEscrowTokenKey;
96
97 // BLE characteristics associated with the enrollment/add escrow token service.
98 private BluetoothGattCharacteristic mEnrolmentTokenHandle;
99 private BluetoothGattCharacteristic mEnrolmentEscrowToken;
100
101 private ParcelUuid mEnrolmentServiceUuid;
102
103 private SimpleBleClient mClient;
104 private Context mContext;
105
106 private TextView mTextView;
107 private Handler mHandler;
108
109 private Button mEnrolButton;
110
111 public PhoneEnrolmentController(Context context) {
112 mContext = context;
113
114 mTokenHandleKey = context.getString(R.string.pref_key_token_handle);
115 mEscrowTokenKey = context.getString(R.string.pref_key_escrow_token);
116
117 mClient = new SimpleBleClient(context);
118 mEnrolmentServiceUuid = new ParcelUuid(
119 UUID.fromString(mContext.getString(R.string.enrollment_service_uuid)));
120 mClient.addCallback(mCallback /* callback */);
121
122 mHandler = new Handler(mContext.getMainLooper());
123 }
124
125 /**
126 * Binds the views to the actions that can be performed by this controller.
127 *
128 * @param textView A text view used to display results from various BLE actions
129 * @param scanButton Button used to start scanning for available BLE devices.
130 * @param enrolButton Button used to send new escrow token to remote device.
131 */
132 public void bind(TextView textView, Button scanButton, Button enrolButton) {
133 mTextView = textView;
134 mEnrolButton = enrolButton;
135
136 scanButton.setOnClickListener((view) -> mClient.start(mEnrolmentServiceUuid));
137
138 mEnrolButton.setEnabled(false);
139 mEnrolButton.setAlpha(0.3f);
140 mEnrolButton.setOnClickListener((view) -> {
141 appendOutputText("Sending new escrow token to remote device");
142
143 byte[] token = generateEscrowToken();
144 sendEnrolmentRequest(token);
145
146 // WARNING: Store the token so it can be used later for unlocking. This token
147 // should NEVER be stored on the device that is being unlocked. It should
148 // always be securely stored on a remote device that will trigger the unlock.
149 storeToken(token);
150 });
151 }
152
153 /**
154 * @return A random byte array that is used as the escrow token for remote device unlock.
155 */
156 private byte[] generateEscrowToken() {
157 Random random = new Random();
158 ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
159 buffer.putLong(0, random.nextLong());
160 return buffer.array();
161 }
162
163 private void sendEnrolmentRequest(byte[] token) {
164 mEnrolmentEscrowToken.setValue(token);
165 mClient.writeCharacteristic(mEnrolmentEscrowToken);
166 storeToken(token);
167 }
168
169 private void storeHandle(long handle) {
170 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
171 prefs.edit().putLong(mTokenHandleKey, handle).apply();
172 }
173
174 private void storeToken(byte[] token) {
175 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
176 String byteArray = Base64.encodeToString(token, Base64.DEFAULT);
177 prefs.edit().putString(mEscrowTokenKey, byteArray).apply();
178 }
179
180 private void appendOutputText(final String text) {
181 mHandler.post(() -> mTextView.append("\n" + text));
182 }
183}
diff --git a/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneUnlockController.java b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneUnlockController.java
new file mode 100644
index 00000000..78e50b41
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/PhoneUnlockController.java
@@ -0,0 +1,149 @@
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 */
16package com.android.car.trust.client;
17
18import android.bluetooth.BluetoothDevice;
19import android.bluetooth.BluetoothGatt;
20import android.bluetooth.BluetoothGattCharacteristic;
21import android.bluetooth.BluetoothGattService;
22import android.content.Context;
23import android.content.SharedPreferences;
24import android.os.Handler;
25import android.os.ParcelUuid;
26import android.preference.PreferenceManager;
27import android.util.Base64;
28import android.util.Log;
29import android.widget.Button;
30import android.widget.TextView;
31
32import java.util.UUID;
33
34/**
35 * A controller that sets up a {@link SimpleBleClient} to connect to the BLE unlock service.
36 */
37public class PhoneUnlockController {
38
39 private final SimpleBleClient.ClientCallback mCallback = new SimpleBleClient.ClientCallback() {
40 @Override
41 public void onDeviceConnected(BluetoothDevice device) {
42 appendOutputText("Device connected: " + device.getName()
43 + " addr: " + device.getAddress());
44 }
45
46 @Override
47 public void onDeviceDisconnected() {
48 appendOutputText("Device disconnected");
49 }
50
51 @Override
52 public void onCharacteristicChanged(BluetoothGatt gatt,
53 BluetoothGattCharacteristic characteristic) {
54 // Not expecting any characteristics changes for the unlocking client.
55 }
56
57 @Override
58 public void onServiceDiscovered(BluetoothGattService service) {
59 if (!service.getUuid().equals(mUnlockServiceUuid.getUuid())) {
60 Log.d(Utils.LOG_TAG, "Service UUID: " + service.getUuid()
61 + " does not match Enrolment UUID " + mUnlockServiceUuid.getUuid());
62 return;
63 }
64
65 Log.d(Utils.LOG_TAG, "Unlock Service # characteristics: "
66 + service.getCharacteristics().size());
67 mUnlockEscrowToken = Utils.getCharacteristic(
68 R.string.unlock_escrow_token_uiid, service, mContext);
69 mUnlockTokenHandle = Utils.getCharacteristic(
70 R.string.unlock_handle_uiid, service, mContext);
71 appendOutputText("Unlock BLE client successfully connected");
72
73 mHandler.post(() -> {
74 // Services are now set up, allow users to enrol new escrow tokens.
75 mUnlockButton.setEnabled(true);
76 mUnlockButton.setAlpha(1.0f);
77 });
78 }
79 };
80
81 private String mTokenHandleKey;
82 private String mEscrowTokenKey;
83
84 // BLE characteristics associated with the enrolment/add escrow token service.
85 private BluetoothGattCharacteristic mUnlockTokenHandle;
86 private BluetoothGattCharacteristic mUnlockEscrowToken;
87
88 private ParcelUuid mUnlockServiceUuid;
89
90 private SimpleBleClient mClient;
91 private Context mContext;
92
93 private TextView mTextView;
94 private Handler mHandler;
95
96 private Button mUnlockButton;
97
98 public PhoneUnlockController(Context context) {
99 mContext = context;
100
101 mTokenHandleKey = context.getString(R.string.pref_key_token_handle);
102 mEscrowTokenKey = context.getString(R.string.pref_key_escrow_token);
103
104 mClient = new SimpleBleClient(context);
105 mUnlockServiceUuid = new ParcelUuid(
106 UUID.fromString(mContext.getString(R.string.unlock_service_uuid)));
107 mClient.addCallback(mCallback /* callback */);
108
109 mHandler = new Handler(mContext.getMainLooper());
110 }
111
112 /**
113 * Binds the views to the actions that can be performed by this controller.
114 *
115 * @param textView A text view used to display results from various BLE actions
116 * @param scanButton Button used to start scanning for available BLE devices.
117 * @param enrolButton Button used to send new escrow token to remote device.
118 */
119 public void bind(TextView textView, Button scanButton, Button enrolButton) {
120 mTextView = textView;
121 mUnlockButton = enrolButton;
122
123 scanButton.setOnClickListener((view) -> mClient.start(mUnlockServiceUuid));
124
125 mUnlockButton.setEnabled(false);
126 mUnlockButton.setAlpha(0.3f);
127 mUnlockButton.setOnClickListener((view) -> {
128 appendOutputText("Sending unlock token and handle to remote device");
129 sendUnlockRequest();
130 });
131 }
132
133 private void sendUnlockRequest() {
134 // Retrieve stored token and handle and write to remote device.
135 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
136 long handle = prefs.getLong(mTokenHandleKey, -1);
137 byte[] token = Base64.decode(prefs.getString(mEscrowTokenKey, null), Base64.DEFAULT);
138
139 mUnlockEscrowToken.setValue(token);
140 mUnlockTokenHandle.setValue(Utils.getBytes(handle));
141
142 mClient.writeCharacteristic(mUnlockEscrowToken);
143 mClient.writeCharacteristic(mUnlockTokenHandle);
144 }
145
146 private void appendOutputText(final String text) {
147 mHandler.post(() -> mTextView.append("\n" + text));
148 }
149}
diff --git a/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/SimpleBleClient.java b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/SimpleBleClient.java
new file mode 100644
index 00000000..c0fecb31
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/SimpleBleClient.java
@@ -0,0 +1,355 @@
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 */
16package com.android.car.trust.client;
17
18import android.bluetooth.BluetoothDevice;
19import android.bluetooth.BluetoothGatt;
20import android.bluetooth.BluetoothGattCallback;
21import android.bluetooth.BluetoothGattCharacteristic;
22import android.bluetooth.BluetoothGattService;
23import android.bluetooth.BluetoothManager;
24import android.bluetooth.BluetoothProfile;
25import android.bluetooth.le.BluetoothLeScanner;
26import android.bluetooth.le.ScanCallback;
27import android.bluetooth.le.ScanFilter;
28import android.bluetooth.le.ScanResult;
29import android.bluetooth.le.ScanSettings;
30import android.content.Context;
31import android.os.Handler;
32import android.os.ParcelUuid;
33import android.util.Log;
34
35import androidx.annotation.NonNull;
36
37import java.util.ArrayList;
38import java.util.List;
39import java.util.Queue;
40import java.util.concurrent.ConcurrentLinkedQueue;
41
42/**
43 * A simple client that supports the scanning and connecting to available BLE devices. Should be
44 * used along with {@link SimpleBleServer}.
45 */
46public class SimpleBleClient {
47 public interface ClientCallback {
48 /**
49 * Called when a device that has a matching service UUID is found.
50 **/
51 void onDeviceConnected(BluetoothDevice device);
52
53 void onDeviceDisconnected();
54
55 void onCharacteristicChanged(BluetoothGatt gatt,
56 BluetoothGattCharacteristic characteristic);
57
58 /**
59 * Called for each {@link BluetoothGattService} that is discovered on the
60 * {@link BluetoothDevice} after a matching scan result and connection.
61 *
62 * @param service {@link BluetoothGattService} that has been discovered.
63 */
64 void onServiceDiscovered(BluetoothGattService service);
65 }
66
67 /**
68 * Wrapper class to allow queuing of BLE actions. The BLE stack allows only one action to be
69 * executed at a time.
70 */
71 public static class BleAction {
72 public static final int ACTION_WRITE = 0;
73 public static final int ACTION_READ = 1;
74
75 private int mAction;
76 private BluetoothGattCharacteristic mCharacteristic;
77
78 public BleAction(BluetoothGattCharacteristic characteristic, int action) {
79 mAction = action;
80 mCharacteristic = characteristic;
81 }
82
83 public int getAction() {
84 return mAction;
85 }
86
87 public BluetoothGattCharacteristic getCharacteristic() {
88 return mCharacteristic;
89 }
90 }
91
92 private static final long SCAN_TIME_MS = 10000;
93
94 private final Queue<BleAction> mBleActionQueue = new ConcurrentLinkedQueue<BleAction>();
95 private final List<ClientCallback> mCallbacks = new ArrayList<>();
96 private final Context mContext;
97 private final BluetoothLeScanner mScanner;
98
99 private BluetoothGatt mBtGatt;
100 private ParcelUuid mServiceUuid;
101
102 public SimpleBleClient(@NonNull Context context) {
103 mContext = context;
104 BluetoothManager btManager = (BluetoothManager) mContext.getSystemService(
105 Context.BLUETOOTH_SERVICE);
106 mScanner = btManager.getAdapter().getBluetoothLeScanner();
107 }
108
109 /**
110 * Start scanning for a BLE devices with the specified service uuid.
111 *
112 * @param parcelUuid {@link ParcelUuid} used to identify the device that should be used for
113 * this client. This uuid should be the same as the one that is set in the
114 * {@link android.bluetooth.le.AdvertiseData.Builder} by the advertising
115 * device.
116 */
117 public void start(ParcelUuid parcelUuid) {
118 mServiceUuid = parcelUuid;
119
120 // We only want to scan for devices that have the correct uuid set in its advertise data.
121 List<ScanFilter> filters = new ArrayList<ScanFilter>();
122 ScanFilter.Builder serviceFilter = new ScanFilter.Builder();
123 serviceFilter.setServiceUuid(mServiceUuid);
124 filters.add(serviceFilter.build());
125
126 ScanSettings.Builder settings = new ScanSettings.Builder();
127 settings.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
128
129 Log.d(Utils.LOG_TAG, "Start scanning for uuid: " + mServiceUuid.getUuid());
130 mScanner.startScan(filters, settings.build(), mScanCallback);
131
132 Handler handler = new Handler();
133 handler.postDelayed(new Runnable() {
134 @Override
135 public void run() {
136 mScanner.stopScan(mScanCallback);
137 Log.d(Utils.LOG_TAG, "Stopping Scanner");
138 }
139 }, SCAN_TIME_MS);
140 }
141
142 private boolean hasServiceUuid(ScanResult result) {
143 if (result.getScanRecord() == null
144 || result.getScanRecord().getServiceUuids() == null
145 || result.getScanRecord().getServiceUuids().size() == 0) {
146 return false;
147 }
148 return true;
149 }
150
151 /**
152 * Writes to a {@link BluetoothGattCharacteristic} if possible, or queues the action until
153 * other actions are complete.
154 *
155 * @param characteristic {@link BluetoothGattCharacteristic} to be written
156 */
157 public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
158 processAction(new BleAction(characteristic, BleAction.ACTION_WRITE));
159 }
160
161 /**
162 * Reads a {@link BluetoothGattCharacteristic} if possible, or queues the read action until
163 * other actions are complete.
164 *
165 * @param characteristic {@link BluetoothGattCharacteristic} to be read.
166 */
167 public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
168 processAction(new BleAction(characteristic, BleAction.ACTION_READ));
169 }
170
171 /**
172 * Enable or disable notification for specified {@link BluetoothGattCharacteristic}.
173 *
174 * @param characteristic The {@link BluetoothGattCharacteristic} for which to enable
175 * notifications.
176 * @param enabled True if notifications should be enabled, false otherwise.
177 */
178 public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
179 boolean enabled) {
180 mBtGatt.setCharacteristicNotification(characteristic, enabled);
181 }
182
183 /**
184 * Add a {@link ClientCallback} to listen for updates from BLE components
185 */
186 public void addCallback(ClientCallback callback) {
187 mCallbacks.add(callback);
188 }
189
190 public void removeCallback(ClientCallback callback) {
191 mCallbacks.remove(callback);
192 }
193
194 private void processAction(BleAction action) {
195 // Only execute actions if the queue is empty.
196 if (mBleActionQueue.size() > 0) {
197 mBleActionQueue.add(action);
198 return;
199 }
200
201 mBleActionQueue.add(action);
202 executeAction(mBleActionQueue.peek());
203 }
204
205 private void processNextAction() {
206 mBleActionQueue.poll();
207 executeAction(mBleActionQueue.peek());
208 }
209
210 private void executeAction(BleAction action) {
211 if (action == null) {
212 return;
213 }
214
215 Log.d(Utils.LOG_TAG, "Executing BLE Action type: " + action.getAction());
216 int actionType = action.getAction();
217 switch (actionType) {
218 case BleAction.ACTION_WRITE:
219 mBtGatt.writeCharacteristic(action.getCharacteristic());
220 break;
221 case BleAction.ACTION_READ:
222 mBtGatt.readCharacteristic(action.getCharacteristic());
223 break;
224 default:
225 }
226 }
227
228 private String getStatus(int status) {
229 switch (status) {
230 case BluetoothGatt.GATT_FAILURE:
231 return "Failure";
232 case BluetoothGatt.GATT_SUCCESS:
233 return "GATT_SUCCESS";
234 case BluetoothGatt.GATT_READ_NOT_PERMITTED:
235 return "GATT_READ_NOT_PERMITTED";
236 case BluetoothGatt.GATT_WRITE_NOT_PERMITTED:
237 return "GATT_WRITE_NOT_PERMITTED";
238 case BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION:
239 return "GATT_INSUFFICIENT_AUTHENTICATION";
240 case BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED:
241 return "GATT_REQUEST_NOT_SUPPORTED";
242 case BluetoothGatt.GATT_INVALID_OFFSET:
243 return "GATT_INVALID_OFFSET";
244 case BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH:
245 return "GATT_INVALID_ATTRIBUTE_LENGTH";
246 case BluetoothGatt.GATT_CONNECTION_CONGESTED:
247 return "GATT_CONNECTION_CONGESTED";
248 default:
249 return "unknown";
250 }
251 }
252
253 private ScanCallback mScanCallback = new ScanCallback() {
254 @Override
255 public void onScanResult(int callbackType, ScanResult result) {
256 BluetoothDevice device = result.getDevice();
257 Log.d(Utils.LOG_TAG, "Scan result found: " + result.getScanRecord().getServiceUuids());
258
259 if (!hasServiceUuid(result)) {
260 return;
261 }
262
263 for (ParcelUuid uuid : result.getScanRecord().getServiceUuids()) {
264 Log.d(Utils.LOG_TAG, "Scan result UUID: " + uuid);
265 if (uuid.equals(mServiceUuid)) {
266 // This client only supports connecting to one service.
267 // Once we find one, stop scanning and open a GATT connection to the device.
268 mScanner.stopScan(mScanCallback);
269 mBtGatt = device.connectGatt(mContext, false /* autoConnect */, mGattCallback);
270 return;
271 }
272 }
273 }
274
275 @Override
276 public void onBatchScanResults(List<ScanResult> results) {
277 for (ScanResult r : results) {
278 Log.d(Utils.LOG_TAG, "Batch scanResult: " + r.getDevice().getName()
279 + " " + r.getDevice().getAddress());
280 }
281 }
282
283 @Override
284 public void onScanFailed(int errorCode) {
285 Log.e(Utils.LOG_TAG, "Scan failed: " + errorCode);
286 }
287 };
288
289 private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
290 @Override
291 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
292 super.onConnectionStateChange(gatt, status, newState);
293
294 String state = "";
295
296 if (newState == BluetoothProfile.STATE_CONNECTED) {
297 state = "Connected";
298 mBtGatt.discoverServices();
299 for (ClientCallback callback : mCallbacks) {
300 callback.onDeviceConnected(gatt.getDevice());
301 }
302
303 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
304 state = "Disconnected";
305 for (ClientCallback callback : mCallbacks) {
306 callback.onDeviceDisconnected();
307 }
308 }
309 Log.d(Utils.LOG_TAG, "Gatt connection status: " + getStatus(status)
310 + " newState: " + state);
311 }
312
313 @Override
314 public void onServicesDiscovered(BluetoothGatt gatt, int status) {
315 super.onServicesDiscovered(gatt, status);
316 Log.d(Utils.LOG_TAG, "onServicesDiscovered: " + status);
317
318 List<BluetoothGattService> services = gatt.getServices();
319 if (services == null || services.size() <= 0) {
320 return;
321 }
322
323 // Notify clients of newly discovered services.
324 for (BluetoothGattService service : mBtGatt.getServices()) {
325 Log.d(Utils.LOG_TAG, "Found service: " + service.getUuid() + " notifying clients");
326 for (ClientCallback callback : mCallbacks) {
327 callback.onServiceDiscovered(service);
328 }
329 }
330 }
331
332 @Override
333 public void onCharacteristicWrite(BluetoothGatt gatt,
334 BluetoothGattCharacteristic characteristic, int status) {
335 Log.d(Utils.LOG_TAG, "onCharacteristicWrite: " + status);
336 processNextAction();
337 }
338
339 @Override
340 public void onCharacteristicRead(BluetoothGatt gatt,
341 BluetoothGattCharacteristic characteristic, int status) {
342 Log.d(Utils.LOG_TAG, "onCharacteristicRead:" + new String(characteristic.getValue()));
343 processNextAction();
344 }
345
346 @Override
347 public void onCharacteristicChanged(BluetoothGatt gatt,
348 BluetoothGattCharacteristic characteristic) {
349 for (ClientCallback callback : mCallbacks) {
350 callback.onCharacteristicChanged(gatt, characteristic);
351 }
352 processNextAction();
353 }
354 };
355}
diff --git a/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/Utils.java b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/Utils.java
new file mode 100644
index 00000000..003a86cc
--- /dev/null
+++ b/tests/CarTrustAgentClientApp/src/com/android/car/trust/client/Utils.java
@@ -0,0 +1,46 @@
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 */
16package com.android.car.trust.client;
17
18import android.bluetooth.BluetoothGattCharacteristic;
19import android.bluetooth.BluetoothGattService;
20import android.content.Context;
21
22import java.nio.ByteBuffer;
23import java.util.UUID;
24
25public class Utils {
26
27 public static final String LOG_TAG = "CarTrustAgentClient";
28
29 public static byte[] getBytes(long l) {
30 ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
31 buffer.putLong(0, l);
32 return buffer.array();
33 }
34
35 public static long getLong(byte[] bytes) {
36 ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
37 buffer.put(bytes);
38 buffer.flip();
39 return buffer.getLong();
40 }
41
42 public static BluetoothGattCharacteristic getCharacteristic(int uuidRes,
43 BluetoothGattService service, Context context) {
44 return service.getCharacteristic(UUID.fromString(context.getString(uuidRes)));
45 }
46}
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index bc89ad43..6e8e80f7 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -42,6 +42,8 @@
42 <uses-permission android:name="android.permission.READ_SMS"/> 42 <uses-permission android:name="android.permission.READ_SMS"/>
43 <uses-permission android:name="android.permission.BLUETOOTH" /> 43 <uses-permission android:name="android.permission.BLUETOOTH" />
44 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 44 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
45 <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
46 <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
45 <uses-permission android:name="android.permission.INJECT_EVENTS" /> 47 <uses-permission android:name="android.permission.INJECT_EVENTS" />
46 48
47 <application android:label="@string/app_title" 49 <application android:label="@string/app_title"
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/connectivity_fragment.xml b/tests/EmbeddedKitchenSinkApp/res/layout/connectivity_fragment.xml
new file mode 100644
index 00000000..15422b11
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/connectivity_fragment.xml
@@ -0,0 +1,55 @@
1<?xml version="1.0" encoding="utf-8"?>
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<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
18 android:orientation="vertical"
19 android:layout_width="match_parent"
20 android:layout_height="match_parent">
21
22 <LinearLayout
23 android:orientation="horizontal"
24 android:layout_width="match_parent"
25 android:layout_height="wrap_content">
26 <ListView
27 android:id="@+id/networks"
28 android:layout_width="wrap_content"
29 android:layout_height="wrap_content">
30 </ListView>
31 </LinearLayout>
32 <LinearLayout
33 android:orientation="horizontal"
34 android:layout_width="wrap_content"
35 android:layout_height="wrap_content"
36 android:layout_margin="4dp">
37 <Button android:id="@+id/networksRefresh"
38 android:layout_width="wrap_content"
39 android:layout_height="wrap_content"
40 android:text="Refresh"/>
41 <Button android:id="@+id/networkRequestOemPaid"
42 android:layout_width="wrap_content"
43 android:layout_height="wrap_content"
44 android:text="Request OEM-paid"/>
45 <Button android:id="@+id/networkRequestEth1"
46 android:layout_width="wrap_content"
47 android:layout_height="wrap_content"
48 android:text="Request eth1"/>
49 <Button android:id="@+id/networkReleaseNetwork"
50 android:layout_width="wrap_content"
51 android:layout_height="wrap_content"
52 android:text="Release Request"/>
53 </LinearLayout>
54
55</LinearLayout> \ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/list_item.xml b/tests/EmbeddedKitchenSinkApp/res/layout/list_item.xml
new file mode 100644
index 00000000..f517913a
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/list_item.xml
@@ -0,0 +1,22 @@
1<?xml version="1.0" encoding="utf-8"?>
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<TextView xmlns:android="http://schemas.android.com/apk/res/android"
17 android:id="@android:id/text1"
18 android:paddingTop="2dip"
19 android:paddingBottom="3dip"
20 android:layout_width="fill_parent"
21 android:layout_height="wrap_content"
22 android:textSize="24sp" /> \ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 74c7c263..452fec8d 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -211,14 +211,14 @@
211 <!-- sensors test --> 211 <!-- sensors test -->
212 <string name="sensor_na">N/A</string> 212 <string name="sensor_na">N/A</string>
213 213
214 <string name="sensor_environment">Environment[%1$s]: temperature=%2$s, pressure=%3$s</string> 214 <string name="sensor_environment">Environment[%1$s]: temperature=%2$s</string>
215 <string name="sensor_night">Night[%1$s]: isNight=%2$s</string> 215 <string name="sensor_night">Night[%1$s]: isNight=%2$s</string>
216 <string name="sensor_gear">Gear[%1$s]: gear=%2$s</string> 216 <string name="sensor_gear">Gear[%1$s]: gear=%2$s</string>
217 <string name="sensor_parking_brake">Parking brake[%1$s]: isEngaged=%2$s</string> 217 <string name="sensor_parking_brake">Parking brake[%1$s]: isEngaged=%2$s</string>
218 <string name="sensor_odometer">Odometer[%1$s]: kms=%2$s</string> 218 <string name="sensor_odometer">Odometer[%1$s]: kms=%2$s</string>
219 <string name="sensor_rpm">RPM[%1$s]: rpm=%2$s</string> 219 <string name="sensor_rpm">RPM[%1$s]: rpm=%2$s</string>
220 <string name="sensor_speed">Speed[%1$s]: speed=%2$s</string> 220 <string name="sensor_speed">Speed[%1$s]: speed=%2$s</string>
221 <string name="sensor_driving_status">Driving status[%1$s]: status=%2$s [bin=%3$s]</string> 221 <string name="sensor_ignition_status">Ignition status[%1$s]: status=%2$s</string>
222 <string name="sensor_compass">Compass[%1$s]: bear=%2$s, pitch=%3$s, roll=%4$s</string> 222 <string name="sensor_compass">Compass[%1$s]: bear=%2$s, pitch=%3$s, roll=%4$s</string>
223 <string name="sensor_accelerometer">Accelerometer[%1$s]: x=%2$s, y=%3$s, z=%4$s</string> 223 <string name="sensor_accelerometer">Accelerometer[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
224 <string name="sensor_gyroscope">Gyroscope[%1$s]: x=%2$s, y=%3$s, z=%4$s</string> 224 <string name="sensor_gyroscope">Gyroscope[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
@@ -231,6 +231,7 @@
231 <string name="sensor_traction_control_is_active">Traction Control[%1$s]: isActive=%2$s</string> 231 <string name="sensor_traction_control_is_active">Traction Control[%1$s]: isActive=%2$s</string>
232 <string name="sensor_fuel_level">Fuel Level[%1$s]: %2$s</string> 232 <string name="sensor_fuel_level">Fuel Level[%1$s]: %2$s</string>
233 <string name="sensor_fuel_door_open">Fuel Door Open[%1$s]: %2$s</string> 233 <string name="sensor_fuel_door_open">Fuel Door Open[%1$s]: %2$s</string>
234 <string name="sensor_engine_oil_level">Engine Oil Level[%1$s]: %2$s</string>
234 <string name="sensor_engine_is_on">Engine Is On[%1$s]: %2$s</string> 235 <string name="sensor_engine_is_on">Engine Is On[%1$s]: %2$s</string>
235 <string name="sensor_ev_battery_level">EV Battery Level[%1$s]: %2$s</string> 236 <string name="sensor_ev_battery_level">EV Battery Level[%1$s]: %2$s</string>
236 <string name="sensor_ev_charge_port_is_open">EV Charge Port Is Open[%1$s]: %2$s</string> 237 <string name="sensor_ev_charge_port_is_open">EV Charge Port Is Open[%1$s]: %2$s</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index 7837f2e7..caca03af 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -23,7 +23,9 @@ import android.car.hardware.power.CarPowerManager;
23import android.car.hardware.property.CarPropertyManager; 23import android.car.hardware.property.CarPropertyManager;
24import android.content.Intent; 24import android.content.Intent;
25import android.content.pm.PackageManager; 25import android.content.pm.PackageManager;
26import android.os.AsyncTask;
26import android.os.Bundle; 27import android.os.Bundle;
28import android.os.Handler;
27import android.support.car.Car; 29import android.support.car.Car;
28import android.support.car.CarAppFocusManager; 30import android.support.car.CarAppFocusManager;
29import android.support.car.CarConnectionCallback; 31import android.support.car.CarConnectionCallback;
@@ -42,6 +44,7 @@ import com.google.android.car.kitchensink.audio.AudioTestFragment;
42import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment; 44import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment;
43import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment; 45import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
44import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment; 46import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment;
47import com.google.android.car.kitchensink.connectivity.ConnectivityFragment;
45import com.google.android.car.kitchensink.cube.CubesTestFragment; 48import com.google.android.car.kitchensink.cube.CubesTestFragment;
46import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment; 49import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment;
47import com.google.android.car.kitchensink.displayinfo.DisplayInfoFragment; 50import com.google.android.car.kitchensink.displayinfo.DisplayInfoFragment;
@@ -167,6 +170,7 @@ public class KitchenSinkActivity extends CarDrawerActivity {
167 startActivity(intent); 170 startActivity(intent);
168 }); 171 });
169 add("activity view", ActivityViewTestFragment.class); 172 add("activity view", ActivityViewTestFragment.class);
173 add("connectivity", ConnectivityFragment.class);
170 add("quit", KitchenSinkActivity.this::finish); 174 add("quit", KitchenSinkActivity.this::finish);
171 } 175 }
172 176
@@ -183,6 +187,7 @@ public class KitchenSinkActivity extends CarDrawerActivity {
183 private CarPropertyManager mPropertyManager; 187 private CarPropertyManager mPropertyManager;
184 private CarSensorManager mSensorManager; 188 private CarSensorManager mSensorManager;
185 private CarAppFocusManager mCarAppFocusManager; 189 private CarAppFocusManager mCarAppFocusManager;
190 private Object mPropertyManagerReady = new Object();
186 191
187 public CarHvacManager getHvacManager() { 192 public CarHvacManager getHvacManager() {
188 return mHvacManager; 193 return mHvacManager;
@@ -212,12 +217,20 @@ public class KitchenSinkActivity extends CarDrawerActivity {
212 setMainContent(R.layout.kitchen_content); 217 setMainContent(R.layout.kitchen_content);
213 // Connection to Car Service does not work for non-automotive yet. 218 // Connection to Car Service does not work for non-automotive yet.
214 if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { 219 if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
215 mCarApi = Car.createCar(this, mCarConnectionCallback); 220 initCarApi();
216 mCarApi.connect();
217 } 221 }
218 Log.i(TAG, "onCreate"); 222 Log.i(TAG, "onCreate");
219 } 223 }
220 224
225 private void initCarApi() {
226 if (mCarApi != null && mCarApi.isConnected()) {
227 mCarApi.disconnect();
228 mCarApi = null;
229 }
230 mCarApi = Car.createCar(this, mCarConnectionCallback);
231 mCarApi.connect();
232 }
233
221 @Override 234 @Override
222 protected void onStart() { 235 protected void onStart() {
223 super.onStart(); 236 super.onStart();
@@ -268,18 +281,22 @@ public class KitchenSinkActivity extends CarDrawerActivity {
268 @Override 281 @Override
269 public void onConnected(Car car) { 282 public void onConnected(Car car) {
270 Log.d(TAG, "Connected to Car Service"); 283 Log.d(TAG, "Connected to Car Service");
271 try { 284 synchronized (mPropertyManagerReady) {
272 mHvacManager = (CarHvacManager) mCarApi.getCarManager(android.car.Car.HVAC_SERVICE); 285 try {
273 mPowerManager = (CarPowerManager) mCarApi.getCarManager( 286 mHvacManager = (CarHvacManager) mCarApi.getCarManager(
274 android.car.Car.POWER_SERVICE); 287 android.car.Car.HVAC_SERVICE);
275 mPropertyManager = (CarPropertyManager) mCarApi.getCarManager( 288 mPowerManager = (CarPowerManager) mCarApi.getCarManager(
276 android.car.Car.PROPERTY_SERVICE); 289 android.car.Car.POWER_SERVICE);
277 mSensorManager = (CarSensorManager) mCarApi.getCarManager( 290 mPropertyManager = (CarPropertyManager) mCarApi.getCarManager(
278 android.car.Car.SENSOR_SERVICE); 291 android.car.Car.PROPERTY_SERVICE);
279 mCarAppFocusManager = 292 mSensorManager = (CarSensorManager) mCarApi.getCarManager(
280 (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE); 293 android.car.Car.SENSOR_SERVICE);
281 } catch (CarNotConnectedException e) { 294 mCarAppFocusManager =
282 Log.e(TAG, "Car is not connected!", e); 295 (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
296 mPropertyManagerReady.notifyAll();
297 } catch (CarNotConnectedException e) {
298 Log.e(TAG, "Car is not connected!", e);
299 }
283 } 300 }
284 } 301 }
285 302
@@ -322,4 +339,29 @@ public class KitchenSinkActivity extends CarDrawerActivity {
322 getDrawerController().closeDrawer(); 339 getDrawerController().closeDrawer();
323 } 340 }
324 } 341 }
342
343 // Use AsyncTask to refresh Car*Manager after car service connected
344 public void requestRefreshManager(final Runnable r, final Handler h) {
345 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
346 @Override
347 protected Void doInBackground(Void... unused) {
348 synchronized (mPropertyManagerReady) {
349 while (!mCarApi.isConnected()) {
350 try {
351 mPropertyManagerReady.wait();
352 } catch (InterruptedException e) {
353 return null;
354 }
355 }
356 }
357 return null;
358 }
359
360 @Override
361 protected void onPostExecute(Void unused) {
362 h.post(r);
363 }
364 };
365 task.execute();
366 }
325} 367}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/connectivity/ConnectivityFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/connectivity/ConnectivityFragment.java
new file mode 100644
index 00000000..0ffa6bf3
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/connectivity/ConnectivityFragment.java
@@ -0,0 +1,154 @@
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
17package com.google.android.car.kitchensink.connectivity;
18
19import android.annotation.Nullable;
20import android.annotation.SuppressLint;
21import android.net.ConnectivityManager;
22import android.net.ConnectivityManager.NetworkCallback;
23import android.net.Network;
24import android.net.NetworkCapabilities;
25import android.net.NetworkInfo;
26import android.net.NetworkRequest;
27import android.os.Bundle;
28import android.os.Handler;
29import android.support.v4.app.Fragment;
30import android.util.Log;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.widget.ArrayAdapter;
35import android.widget.ListView;
36import android.widget.Toast;
37
38import com.google.android.car.kitchensink.R;
39
40import java.util.ArrayList;
41
42@SuppressLint("SetTextI18n")
43public class ConnectivityFragment extends Fragment {
44 private static final String TAG = ConnectivityFragment.class.getSimpleName();
45
46 private final Handler mHandler = new Handler();
47 private final ArrayList<String> mNetworks = new ArrayList<>();
48
49 private ConnectivityManager mConnectivityManager;
50 private ArrayAdapter<String> mNetworksAdapter;
51
52 private final NetworkCallback mNetworkCallback = new NetworkCallback() {
53 @Override
54 public void onAvailable(Network network) {
55 showToast("onAvailable, netId: " + network);
56 refreshNetworks();
57 }
58
59 @Override
60 public void onLost(Network network) {
61 showToast("onLost, netId: " + network);
62 refreshNetworks();
63 }
64 };
65
66 @Override
67 public void onCreate(Bundle savedInstanceState) {
68 super.onCreate(savedInstanceState);
69
70 mConnectivityManager = getActivity().getSystemService(ConnectivityManager.class);
71
72 mConnectivityManager.addDefaultNetworkActiveListener(() -> refreshNetworks());
73 }
74
75 @Nullable
76 @Override
77 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
78 @Nullable Bundle savedInstanceState) {
79 View view = inflater.inflate(R.layout.connectivity_fragment, container, false);
80
81 ListView networksView = view.findViewById(R.id.networks);
82 mNetworksAdapter = new ArrayAdapter<>(getActivity(), R.layout.list_item, mNetworks);
83 networksView.setAdapter(mNetworksAdapter);
84
85 setClickAction(view, R.id.networksRefresh, this::refreshNetworks);
86 setClickAction(view, R.id.networkRequestOemPaid, this::requestOemPaid);
87 setClickAction(view, R.id.networkRequestEth1, this::requestEth1);
88 setClickAction(view, R.id.networkReleaseNetwork, this::releaseNetworkRequest);
89
90 return view;
91 }
92
93 private void releaseNetworkRequest() {
94 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
95 showToast("Release request sent");
96 }
97
98 private void requestEth1() {
99 NetworkRequest request = new NetworkRequest.Builder()
100 .clearCapabilities()
101 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
102 .setNetworkSpecifier("eth1")
103 .build();
104 mConnectivityManager.requestNetwork(request, mNetworkCallback, mHandler);
105 }
106
107 private void requestOemPaid() {
108 NetworkRequest request = new NetworkRequest.Builder()
109 .addCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)
110 .build();
111
112 mConnectivityManager.requestNetwork(request, mNetworkCallback, mHandler);
113 }
114
115 @Override
116 public void onResume() {
117 super.onResume();
118 refreshNetworks();
119 }
120
121 private void setClickAction(View view, int id, Runnable action) {
122 view.findViewById(id).setOnClickListener(v -> action.run());
123 }
124
125 private void refreshNetworks() {
126 mNetworks.clear();
127
128 for (Network network : mConnectivityManager.getAllNetworks()) {
129 boolean isDefault = sameNetworkId(network, mConnectivityManager.getActiveNetwork());
130 NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(network);
131 boolean isOemPaid = nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID);
132 boolean isInternet = nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
133
134 NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
135
136 mNetworks.add("netId: " + network.netId
137 + (isInternet ? " [INTERNET]" : "")
138 + (isDefault ? " [DEFAULT]" : "")
139 + (isOemPaid ? " [OEM-paid]" : "") + nc + " " + networkInfo);
140 }
141
142 mNetworksAdapter.notifyDataSetChanged();
143 }
144
145 private void showToast(String text) {
146 Log.d(TAG, "showToast: " + text);
147 Toast.makeText(getContext(), text, Toast.LENGTH_LONG).show();
148 }
149
150 private static boolean sameNetworkId(Network net1, Network net2) {
151 return net1 != null && net2 != null && net1.netId == net2.netId;
152
153 }
154}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
index a1f8e1de..c7b80e84 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
@@ -25,6 +25,7 @@ import android.car.hardware.hvac.CarHvacManager;
25import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat; 25import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat;
26import android.hardware.automotive.vehicle.V2_0.VehicleAreaWindow; 26import android.hardware.automotive.vehicle.V2_0.VehicleAreaWindow;
27import android.os.Bundle; 27import android.os.Bundle;
28import android.os.Handler;
28import android.support.v4.app.Fragment; 29import android.support.v4.app.Fragment;
29import android.util.Log; 30import android.util.Log;
30import android.view.LayoutInflater; 31import android.view.LayoutInflater;
@@ -76,6 +77,8 @@ public class HvacTestFragment extends Fragment {
76 private int mZoneForSetTempP; 77 private int mZoneForSetTempP;
77 private int mZoneForFanSpeed; 78 private int mZoneForFanSpeed;
78 private int mZoneForFanPosition; 79 private int mZoneForFanPosition;
80 private List<CarPropertyConfig> mCarPropertyConfigs;
81 private View mHvacView;
79 82
80 private final CarHvacManager.CarHvacEventCallback mHvacCallback = 83 private final CarHvacManager.CarHvacEventCallback mHvacCallback =
81 new CarHvacManager.CarHvacEventCallback () { 84 new CarHvacManager.CarHvacEventCallback () {
@@ -171,13 +174,9 @@ public class HvacTestFragment extends Fragment {
171 174
172 @Override 175 @Override
173 public void onCreate(Bundle savedInstanceState) { 176 public void onCreate(Bundle savedInstanceState) {
174 mCarHvacManager = ((KitchenSinkActivity)getActivity()).getHvacManager(); 177
175 super.onCreate(savedInstanceState); 178 super.onCreate(savedInstanceState);
176 try { 179
177 mCarHvacManager.registerCallback(mHvacCallback);
178 } catch (CarNotConnectedException e) {
179 Log.e(TAG, "Car is not connected!");
180 }
181 } 180 }
182 181
183 @Override 182 @Override
@@ -188,77 +187,85 @@ public class HvacTestFragment extends Fragment {
188 187
189 @Override 188 @Override
190 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) { 189 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
191 View v = inflater.inflate(R.layout.hvac_test, container, false); 190 mHvacView = inflater.inflate(R.layout.hvac_test, container, false);
192 191 final Runnable r = () -> {
193 List<CarPropertyConfig> props; 192 mCarHvacManager = ((KitchenSinkActivity) getActivity()).getHvacManager();
194 try { 193 try {
195 props = mCarHvacManager.getPropertyList(); 194 mCarHvacManager.registerCallback(mHvacCallback);
196 } catch (CarNotConnectedException e) { 195 } catch (CarNotConnectedException e) {
197 Log.e(TAG, "Failed to get list of properties", e); 196 Log.e(TAG, "Car is not connected!");
198 props = new ArrayList<>(); 197 }
199 } 198 try {
199 mCarPropertyConfigs = mCarHvacManager.getPropertyList();
200 } catch (CarNotConnectedException e) {
201 Log.e(TAG, "Failed to get list of properties", e);
202 mCarPropertyConfigs = new ArrayList<>();
203 }
204 for (CarPropertyConfig prop : mCarPropertyConfigs) {
205 int propId = prop.getPropertyId();
200 206
201 for(CarPropertyConfig prop : props) { 207 if (DBG) {
202 int propId = prop.getPropertyId(); 208 Log.d(TAG, prop.toString());
209 }
203 210
204 if(DBG) { 211 switch(propId) {
205 Log.d(TAG, prop.toString()); 212 case CarHvacManager.ID_OUTSIDE_AIR_TEMP:
213 configureOutsideTemp(mHvacView, prop);
214 break;
215 case CarHvacManager.ID_ZONED_DUAL_ZONE_ON:
216 configureDualOn(mHvacView, prop);
217 break;
218 case CarHvacManager.ID_ZONED_AC_ON:
219 configureAcOn(mHvacView, prop);
220 break;
221 case CarHvacManager.ID_ZONED_FAN_DIRECTION:
222 configureFanPosition(mHvacView, prop);
223 break;
224 case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT:
225 configureFanSpeed(mHvacView, prop);
226 break;
227 case CarHvacManager.ID_ZONED_TEMP_SETPOINT:
228 configureTempSetpoint(mHvacView, prop);
229 break;
230 case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON:
231 configureAutoModeOn(mHvacView, prop);
232 break;
233 case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON:
234 configureRecircOn(mHvacView, prop);
235 break;
236 case CarHvacManager.ID_ZONED_MAX_AC_ON:
237 configureMaxAcOn(mHvacView, prop);
238 break;
239 case CarHvacManager.ID_ZONED_MAX_DEFROST_ON:
240 configureMaxDefrostOn(mHvacView, prop);
241 break;
242 case CarHvacManager.ID_WINDOW_DEFROSTER_ON:
243 configureDefrosterOn(mHvacView, prop);
244 break;
245 default:
246 Log.w(TAG, "propertyId " + propId + " is not handled");
247 break;
248 }
206 } 249 }
207 250
208 switch(propId) { 251 mTvFanSpeed = (TextView) mHvacView.findViewById(R.id.tvFanSpeed);
209 case CarHvacManager.ID_OUTSIDE_AIR_TEMP: 252 mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
210 configureOutsideTemp(v, prop); 253 mTvDTemp = (TextView) mHvacView.findViewById(R.id.tvDTemp);
211 break; 254 mTvDTemp.setText(String.valueOf(mCurDTemp));
212 case CarHvacManager.ID_ZONED_DUAL_ZONE_ON: 255 mTvPTemp = (TextView) mHvacView.findViewById(R.id.tvPTemp);
213 configureDualOn(v, prop); 256 mTvPTemp.setText(String.valueOf(mCurPTemp));
214 break; 257 mTvOutsideTemp = (TextView) mHvacView.findViewById(R.id.tvOutsideTemp);
215 case CarHvacManager.ID_ZONED_AC_ON: 258 mTvOutsideTemp.setText("N/A");
216 configureAcOn(v, prop); 259 };
217 break;
218 case CarHvacManager.ID_ZONED_FAN_DIRECTION:
219 configureFanPosition(v, prop);
220 break;
221 case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT:
222 configureFanSpeed(v, prop);
223 break;
224 case CarHvacManager.ID_ZONED_TEMP_SETPOINT:
225 configureTempSetpoint(v, prop);
226 break;
227 case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON:
228 configureAutoModeOn(v, prop);
229 break;
230 case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON:
231 configureRecircOn(v, prop);
232 break;
233 case CarHvacManager.ID_ZONED_MAX_AC_ON:
234 configureMaxAcOn(v, prop);
235 break;
236 case CarHvacManager.ID_ZONED_MAX_DEFROST_ON:
237 configureMaxDefrostOn(v, prop);
238 break;
239 case CarHvacManager.ID_WINDOW_DEFROSTER_ON:
240 configureDefrosterOn(v, prop);
241 break;
242 default:
243 Log.w(TAG, "propertyId " + propId + " is not handled");
244 break;
245 }
246 }
247 260
248 mTvFanSpeed = (TextView) v.findViewById(R.id.tvFanSpeed); 261 ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
249 mTvFanSpeed.setText(String.valueOf(mCurFanSpeed)); 262 new Handler(getContext().getMainLooper()));
250 mTvDTemp = (TextView) v.findViewById(R.id.tvDTemp);
251 mTvDTemp.setText(String.valueOf(mCurDTemp));
252 mTvPTemp = (TextView) v.findViewById(R.id.tvPTemp);
253 mTvPTemp.setText(String.valueOf(mCurPTemp));
254 mTvOutsideTemp = (TextView) v.findViewById(R.id.tvOutsideTemp);
255 mTvOutsideTemp.setText("N/A");
256 263
257 if(DBG) { 264 if(DBG) {
258 Log.d(TAG, "Starting HvacTestFragment"); 265 Log.d(TAG, "Starting HvacTestFragment");
259 } 266 }
260 267
261 return v; 268 return mHvacView;
262 } 269 }
263 270
264 private void configureOutsideTemp(View v, CarPropertyConfig prop) { 271 private void configureOutsideTemp(View v, CarPropertyConfig prop) {
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/power/PowerTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/power/PowerTestFragment.java
index fed1fbd3..9a6c2b92 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/power/PowerTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/power/PowerTestFragment.java
@@ -20,6 +20,7 @@ import android.car.CarNotConnectedException;
20import android.car.hardware.power.CarPowerManager; 20import android.car.hardware.power.CarPowerManager;
21import android.content.Context; 21import android.content.Context;
22import android.os.Bundle; 22import android.os.Bundle;
23import android.os.Handler;
23import android.os.PowerManager; 24import android.os.PowerManager;
24import android.os.SystemClock; 25import android.os.SystemClock;
25import android.support.v4.app.Fragment; 26import android.support.v4.app.Fragment;
@@ -58,16 +59,20 @@ public class PowerTestFragment extends Fragment {
58 59
59 @Override 60 @Override
60 public void onCreate(Bundle savedInstanceState) { 61 public void onCreate(Bundle savedInstanceState) {
61 mCarPowerManager = ((KitchenSinkActivity)getActivity()).getPowerManager(); 62 final Runnable r = () -> {
62 mExecutor = new ThreadPerTaskExecutor(); 63 mCarPowerManager = ((KitchenSinkActivity) getActivity()).getPowerManager();
64 mExecutor = new ThreadPerTaskExecutor();
65 try {
66 mCarPowerManager.setListener(mPowerListener, mExecutor);
67 } catch (CarNotConnectedException e) {
68 Log.e(TAG, "Car is not connected!");
69 } catch (IllegalStateException e) {
70 Log.e(TAG, "CarPowerManager listener was not cleared");
71 }
72 };
73 ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
74 new Handler(getContext().getMainLooper()));
63 super.onCreate(savedInstanceState); 75 super.onCreate(savedInstanceState);
64 try {
65 mCarPowerManager.setListener(mPowerListener, mExecutor);
66 } catch (CarNotConnectedException e) {
67 Log.e(TAG, "Car is not connected!");
68 } catch (IllegalStateException e) {
69 Log.e(TAG, "CarPowerManager listener was not cleared");
70 }
71 } 76 }
72 77
73 @Override 78 @Override
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java
index fc6621a7..ff1c402f 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/property/PropertyTestFragment.java
@@ -26,6 +26,7 @@ import android.content.DialogInterface.OnClickListener;
26import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 26import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
27import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType; 27import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
28import android.os.Bundle; 28import android.os.Bundle;
29import android.os.Handler;
29import android.support.v4.app.Fragment; 30import android.support.v4.app.Fragment;
30import android.util.Log; 31import android.util.Log;
31import android.view.LayoutInflater; 32import android.view.LayoutInflater;
@@ -85,20 +86,23 @@ public class PropertyTestFragment extends Fragment implements OnItemSelectedList
85 mPropertyId = view.findViewById(R.id.sPropertyId); 86 mPropertyId = view.findViewById(R.id.sPropertyId);
86 mScrollView = view.findViewById(R.id.svEventLog); 87 mScrollView = view.findViewById(R.id.svEventLog);
87 mSetValue = view.findViewById(R.id.etSetPropertyValue); 88 mSetValue = view.findViewById(R.id.etSetPropertyValue);
88 89 mActivity = (KitchenSinkActivity) getActivity();
89 populateConfigList(); 90
90 mListView.setAdapter(new PropertyListAdapter(mPropInfo, mMgr, mEventLog, mScrollView, 91 final Runnable r = () -> {
91 mActivity)); 92 mMgr = mActivity.getPropertyManager();
92 93 populateConfigList();
93 // Configure dropdown menu for propertyId spinner 94 mListView.setAdapter(new PropertyListAdapter(mPropInfo, mMgr, mEventLog, mScrollView,
94 ArrayAdapter<PropertyInfo> adapter = 95 mActivity));
95 new ArrayAdapter<PropertyInfo>(mActivity, android.R.layout.simple_spinner_item, 96
96 mPropInfo); 97 // Configure dropdown menu for propertyId spinner
97 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 98 ArrayAdapter<PropertyInfo> adapter =
98 mPropertyId.setAdapter(adapter); 99 new ArrayAdapter<PropertyInfo>(mActivity, android.R.layout.simple_spinner_item,
99 mPropertyId.setOnItemSelectedListener(this); 100 mPropInfo);
100 101 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
101 102 mPropertyId.setAdapter(adapter);
103 mPropertyId.setOnItemSelectedListener(this);
104 };
105 mActivity.requestRefreshManager(r, new Handler(getContext().getMainLooper()));
102 106
103 // Configure listeners for buttons 107 // Configure listeners for buttons
104 Button b = view.findViewById(R.id.bGetProperty); 108 Button b = view.findViewById(R.id.bGetProperty);
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
index abc2c10b..1440ff05 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
@@ -99,17 +99,19 @@ public class SensorsTestFragment extends Fragment {
99 99
100 View view = inflater.inflate(R.layout.sensors, container, false); 100 View view = inflater.inflate(R.layout.sensors, container, false);
101 mActivity = (KitchenSinkActivity) getHost(); 101 mActivity = (KitchenSinkActivity) getHost();
102
103 mSensorInfo = (TextView) view.findViewById(R.id.sensor_info); 102 mSensorInfo = (TextView) view.findViewById(R.id.sensor_info);
104 mNaString = getContext().getString(R.string.sensor_na); 103 mNaString = getContext().getString(R.string.sensor_na);
105
106 return view; 104 return view;
107 } 105 }
108 106
109 @Override 107 @Override
110 public void onResume() { 108 public void onResume() {
111 super.onResume(); 109 super.onResume();
112 initPermissions(); 110 final Runnable r = () -> {
111 initPermissions();
112 };
113 ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
114 new Handler(getContext().getMainLooper()));
113 } 115 }
114 116
115 @Override 117 @Override
@@ -207,6 +209,12 @@ public class SensorsTestFragment extends Fragment {
207 case CarSensorManager.SENSOR_TYPE_FUEL_DOOR_OPEN: 209 case CarSensorManager.SENSOR_TYPE_FUEL_DOOR_OPEN:
208 summary.add(getFuelDoorOpen(event)); 210 summary.add(getFuelDoorOpen(event));
209 break; 211 break;
212 case CarSensorManager.SENSOR_TYPE_IGNITION_STATE:
213 summary.add(getContext().getString(R.string.sensor_ignition_status,
214 getTimestamp(event),
215 event == null ? mNaString :
216 event.getIgnitionStateData(null).ignitionState));
217 break;
210 case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE: 218 case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
211 summary.add(getContext().getString(R.string.sensor_parking_brake, 219 summary.add(getContext().getString(R.string.sensor_parking_brake,
212 getTimestamp(event), 220 getTimestamp(event),
@@ -223,18 +231,15 @@ public class SensorsTestFragment extends Fragment {
223 getTimestamp(event), 231 getTimestamp(event),
224 event == null ? mNaString : event.getNightData(null).isNightMode)); 232 event == null ? mNaString : event.getNightData(null).isNightMode));
225 break; 233 break;
226 case CarSensorManager.SENSOR_TYPE_ENVIRONMENT: 234 case CarSensorManager.SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE:
227 String temperature = mNaString; 235 String temperature = mNaString;
228 String pressure = mNaString;
229 if (event != null) { 236 if (event != null) {
230 CarSensorEvent.EnvironmentData env = event.getEnvironmentData(null); 237 CarSensorEvent.EnvironmentData env = event.getEnvironmentData(null);
231 temperature = Float.isNaN(env.temperature) ? temperature : 238 temperature = Float.isNaN(env.temperature) ? temperature :
232 String.valueOf(env.temperature); 239 String.valueOf(env.temperature);
233 pressure = Float.isNaN(env.pressure) ? pressure :
234 String.valueOf(env.pressure);
235 } 240 }
236 summary.add(getContext().getString(R.string.sensor_environment, 241 summary.add(getContext().getString(R.string.sensor_environment,
237 getTimestamp(event), temperature, pressure)); 242 getTimestamp(event), temperature));
238 break; 243 break;
239 case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE: 244 case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
240 if(event != null) { 245 if(event != null) {
diff --git a/tests/carservice_test/AndroidManifest.xml b/tests/carservice_test/AndroidManifest.xml
index 2be5a830..19a5552e 100644
--- a/tests/carservice_test/AndroidManifest.xml
+++ b/tests/carservice_test/AndroidManifest.xml
@@ -19,7 +19,10 @@
19 19
20 <uses-permission android:name="android.Manifest.permission.MODIFY_AUDIO_ROUTING" /> 20 <uses-permission android:name="android.Manifest.permission.MODIFY_AUDIO_ROUTING" />
21 <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" /> 21 <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
22 <uses-permission android:name="android.car.permission.ADJUST_CAR_CABIN" /> 22 <uses-permission android:name="android.car.permission.CONTROL_CAR_DOORS" />
23 <uses-permission android:name="android.car.permission.CONTROL_CAR_WINDOWS" />
24 <uses-permission android:name="android.car.permission.CONTROL_CAR_MIRRORS" />
25 <uses-permission android:name="android.car.permission.CONTROL_CAR_SEATS" />
23 <uses-permission android:name="android.car.permission.CAR_ENERGY" /> 26 <uses-permission android:name="android.car.permission.CAR_ENERGY" />
24 <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" /> 27 <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" />
25 <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> 28 <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
diff --git a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index a57d800b..c63c980f 100644
--- a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -173,7 +173,7 @@ public class MockedCarTestBase {
173 protected MockContext getCarServiceContext() throws NameNotFoundException { 173 protected MockContext getCarServiceContext() throws NameNotFoundException {
174 if (mMockContext == null) { 174 if (mMockContext == null) {
175 mMockContext = new MockContext(getContext() 175 mMockContext = new MockContext(getContext()
176 .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY)); 176 .createPackageContext("com.android.car.test", Context.CONTEXT_IGNORE_SECURITY));
177 } 177 }
178 return mMockContext; 178 return mMockContext;
179 } 179 }
@@ -393,6 +393,8 @@ public class MockedCarTestBase {
393 switch (name) { 393 switch (name) {
394 case BLUETOOTH_SERVICE: 394 case BLUETOOTH_SERVICE:
395 return CarServiceTestApp.getAppContext().getSystemService(name); 395 return CarServiceTestApp.getAppContext().getSystemService(name);
396 case AUDIO_SERVICE:
397 return CarServiceTestApp.getAppContext().getSystemService(name);
396 default: 398 default:
397 return super.getSystemService(name); 399 return super.getSystemService(name);
398 } 400 }
diff --git a/tests/carservice_test/src/com/android/car/GarageModeTest.java b/tests/carservice_test/src/com/android/car/garagemode/GarageModeServiceTest.java
index 23153fc7..8a9a90d1 100644
--- a/tests/carservice_test/src/com/android/car/GarageModeTest.java
+++ b/tests/carservice_test/src/com/android/car/garagemode/GarageModeServiceTest.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2016 The Android Open Source Project 2 * Copyright (C) 2018 The Android Open Source Project
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -13,12 +13,10 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16package com.android.car;
17 16
18import static org.junit.Assert.assertArrayEquals; 17package com.android.car.garagemode;
18
19import static org.junit.Assert.assertEquals; 19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertNull;
22import static org.junit.Assert.assertTrue; 20import static org.junit.Assert.assertTrue;
23 21
24import android.car.settings.CarSettings; 22import android.car.settings.CarSettings;
@@ -31,16 +29,19 @@ import android.support.test.annotation.UiThreadTest;
31import android.support.test.filters.MediumTest; 29import android.support.test.filters.MediumTest;
32import android.support.test.runner.AndroidJUnit4; 30import android.support.test.runner.AndroidJUnit4;
33 31
34import com.android.car.GarageModeService.GarageModePolicy; 32import com.android.car.CarPowerManagementService;
35import com.android.car.GarageModeService.WakeupTime; 33import com.android.car.DeviceIdleControllerWrapper;
34import com.android.car.R;
36 35
37import org.junit.Test; 36import org.junit.Test;
38import org.junit.runner.RunWith; 37import org.junit.runner.RunWith;
39 38
39import java.util.List;
40
40@RunWith(AndroidJUnit4.class) 41@RunWith(AndroidJUnit4.class)
41@MediumTest 42@MediumTest
42public class GarageModeTest { 43public class GarageModeServiceTest {
43 private static final int WAIT_FOR_COMPLETION_TIME = 3000;//ms 44 private static final int WAIT_FOR_COMPLETION_TIME_MS = 3000;
44 45
45 @Test 46 @Test
46 @UiThreadTest 47 @UiThreadTest
@@ -93,7 +94,7 @@ public class GarageModeTest {
93 powerManagementService.doNotifyPrepareShutdown(false); 94 powerManagementService.doNotifyPrepareShutdown(false);
94 assertTrue(garageMode.getGarageModeIndex() > 0); 95 assertTrue(garageMode.getGarageModeIndex() > 0);
95 powerManagementService.doNotifyPowerOn(true); 96 powerManagementService.doNotifyPowerOn(true);
96 assertEquals(0,garageMode.getGarageModeIndex()); 97 assertEquals(0, garageMode.getGarageModeIndex());
97 } 98 }
98 99
99 @Test 100 @Test
@@ -111,11 +112,11 @@ public class GarageModeTest {
111 powerManagementService, 112 powerManagementService,
112 controller, 113 controller,
113 thread.getLooper()); 114 thread.getLooper());
114 String[] policy = { 115 GarageModePolicy policy = new GarageModePolicy(new String[] {
115 "15m,1", 116 "15m,1",
116 "6h,8", 117 "6h,8",
117 "1d,5", 118 "1d,5",
118 }; 119 });
119 SharedPreferences prefs = 120 SharedPreferences prefs =
120 getContext().getSharedPreferences("testPolicy", Context.MODE_PRIVATE); 121 getContext().getSharedPreferences("testPolicy", Context.MODE_PRIVATE);
121 prefs.edit().putInt("garage_mode_index", 0).apply(); 122 prefs.edit().putInt("garage_mode_index", 0).apply();
@@ -125,7 +126,7 @@ public class GarageModeTest {
125 garageMode.onPrepareShutdown(false); 126 garageMode.onPrepareShutdown(false);
126 garageMode.onShutdown(); 127 garageMode.onShutdown();
127 assertEquals(6 * 60 * 60, garageMode.getWakeupTime()); 128 assertEquals(6 * 60 * 60, garageMode.getWakeupTime());
128 Thread.sleep(WAIT_FOR_COMPLETION_TIME); 129 Thread.sleep(WAIT_FOR_COMPLETION_TIME_MS);
129 assertEquals(1, prefs.getInt("garage_mode_index", 0)); 130 assertEquals(1, prefs.getInt("garage_mode_index", 0));
130 131
131 garageMode = new GarageModeServiceForTest(getContext(), 132 garageMode = new GarageModeServiceForTest(getContext(),
@@ -141,84 +142,78 @@ public class GarageModeTest {
141 garageMode.onPrepareShutdown(false); 142 garageMode.onPrepareShutdown(false);
142 garageMode.onShutdown(); 143 garageMode.onShutdown();
143 assertEquals(24 * 60 * 60, garageMode.getWakeupTime()); 144 assertEquals(24 * 60 * 60, garageMode.getWakeupTime());
144 Thread.sleep(WAIT_FOR_COMPLETION_TIME); 145 Thread.sleep(WAIT_FOR_COMPLETION_TIME_MS);
145 assertEquals(9, prefs.getInt("garage_mode_index", 0)); 146 assertEquals(9, prefs.getInt("garage_mode_index", 0));
146 } 147 }
147 148
148 @Test 149 @Test
149 public void testPolicyParserValid() throws Exception { 150 public void testPolicyParserValid() throws Exception {
150 WakeupTime expected[] = new WakeupTime[]{ 151 WakeupInterval[] expected = new WakeupInterval[] {
151 new WakeupTime(15 * 60, 1), 152 new WakeupInterval(15 * 60, 1),
152 new WakeupTime(6 * 60 * 60, 8), 153 new WakeupInterval(6 * 60 * 60, 8),
153 new WakeupTime(24 * 60 * 60, 5), 154 new WakeupInterval(24 * 60 * 60, 5),
154 }; 155 };
155 WakeupTime received[] = new GarageModePolicy(new String[] { 156 List<WakeupInterval> received = new GarageModePolicy(new String[] {
156 "15m,1", 157 "15m,1",
157 "6h,8", 158 "6h,8",
158 "1d,5", 159 "1d,5",
159 }).mWakeupTime; 160 }).getWakeupIntervals();
160 161
161 assertEquals(expected.length, received.length); 162 assertEquals(expected.length, received.size());
162 for (int i = 0; i < expected.length; i++) { 163 for (int i = 0; i < expected.length; i++) {
163 assertEquals(expected[i].mWakeupTime, received[i].mWakeupTime); 164 assertEquals(expected[i].getWakeupInterval(), received.get(i).getWakeupInterval());
164 assertEquals(expected[i].mNumAttempts, received[i].mNumAttempts); 165 assertEquals(expected[i].getNumAttempts(), received.get(i).getNumAttempts());
165 } 166 }
166 } 167 }
167 168
168 @Test(expected=RuntimeException.class) 169 @Test
169 public void testPolicyParserNull() { 170 public void testPolicyParser() {
170 new GarageModePolicy(null); 171 GarageModePolicy policy;
171 } 172
172 @Test(expected=RuntimeException.class) 173 policy = new GarageModePolicy(null);
173 public void testPolicyParserEmptyArray() { 174 assertEquals(0, policy.getWakeupIntervals().size());
174 new GarageModePolicy(new String[] {}); 175
175 } 176 policy = new GarageModePolicy(new String[] {});
176 @Test(expected=RuntimeException.class) 177 assertEquals(0, policy.getWakeupIntervals().size());
177 public void testPolicyParserEmptyString() { 178
178 new GarageModePolicy(new String[] {""}); 179 policy = new GarageModePolicy(new String[] {""});
179 } 180 assertEquals(0, policy.getWakeupIntervals().size());
180 @Test(expected=RuntimeException.class) 181
181 public void testPolicyParserMissingUnits() { 182 policy = new GarageModePolicy(new String[] {"15,1"});
182 new GarageModePolicy(new String[] {"15,1"}); 183 assertEquals(0, policy.getWakeupIntervals().size());
183 } 184
184 @Test(expected=RuntimeException.class) 185 policy = new GarageModePolicy(new String[] {"15y,1"});
185 public void testPolicyParserInvalidUnits() { 186 assertEquals(0, policy.getWakeupIntervals().size());
186 new GarageModePolicy(new String[] {"15y,1"}); 187
187 } 188 policy = new GarageModePolicy(new String[] {"15m"});
188 @Test(expected=RuntimeException.class) 189 assertEquals(0, policy.getWakeupIntervals().size());
189 public void testPolicyParserNoCount() { 190
190 new GarageModePolicy(new String[] {"15m"}); 191 policy = new GarageModePolicy(new String[] {"15m,Q"});
191 } 192 assertEquals(0, policy.getWakeupIntervals().size());
192 @Test(expected=RuntimeException.class) 193
193 public void testPolicyParserBadCount() { 194 policy = new GarageModePolicy(new String[] {"15m,-1"});
194 new GarageModePolicy(new String[] {"15m,Q"}); 195 assertEquals(0, policy.getWakeupIntervals().size());
195 } 196
196 @Test(expected=RuntimeException.class) 197 policy = new GarageModePolicy(new String[] {",1"});
197 public void testPolicyParserNegativeCount() { 198 assertEquals(0, policy.getWakeupIntervals().size());
198 new GarageModePolicy(new String[] {"15m,-1"}); 199
199 } 200 policy = new GarageModePolicy(new String[] {"m,1"});
200 @Test(expected=RuntimeException.class) 201 assertEquals(0, policy.getWakeupIntervals().size());
201 public void testPolicyParserNoTime() { 202
202 new GarageModePolicy(new String[] {",1"}); 203 policy = new GarageModePolicy(new String[] {"Qm,1"});
203 } 204 assertEquals(0, policy.getWakeupIntervals().size());
204 @Test(expected=RuntimeException.class) 205
205 public void testPolicyParserNoTimeValue() { 206 policy = new GarageModePolicy(new String[] {"-10m,1"});
206 new GarageModePolicy(new String[] {"m,1"}); 207 assertEquals(0, policy.getWakeupIntervals().size());
207 } 208
208 @Test(expected=RuntimeException.class)
209 public void testPolicyParserBadTime() {
210 new GarageModePolicy(new String[] {"Qm,1"});
211 }
212 @Test(expected=RuntimeException.class)
213 public void testPolicyParserNegativeTime() {
214 new GarageModePolicy(new String[] {"-10m,1"});
215 } 209 }
216 210
217 @Test 211 @Test
218 public void testPolicyInResource() throws Exception { 212 public void testPolicyInResource() throws Exception {
219 // Test that the policy in the resource file parses fine. 213 // Test that the policy in the resource file parses fine.
220 assertNotNull(new GarageModePolicy(getContext().getResources().getStringArray( 214 GarageModePolicy policy = new GarageModePolicy(getContext().getResources().getStringArray(
221 R.array.config_garageModeCadence)).mWakeupTime); 215 R.array.config_garageModeCadence));
216 assertTrue(policy.getWakeupIntervals().size() > 0);
222 } 217 }
223 218
224 private static class MockCarPowerManagementService extends CarPowerManagementService { 219 private static class MockCarPowerManagementService extends CarPowerManagementService {
@@ -232,14 +227,14 @@ public class GarageModeTest {
232 } 227 }
233 228
234 private static class GarageModeServiceForTest extends GarageModeService { 229 private static class GarageModeServiceForTest extends GarageModeService {
235 public GarageModeServiceForTest(Context context, 230 GarageModeServiceForTest(Context context,
236 CarPowerManagementService powerManagementService, 231 CarPowerManagementService powerManagementService,
237 DeviceIdleControllerWrapper controllerWrapper, 232 DeviceIdleControllerWrapper controllerWrapper,
238 Looper looper) { 233 Looper looper) {
239 super(context, powerManagementService, controllerWrapper, looper); 234 super(context, powerManagementService, controllerWrapper, looper);
240 } 235 }
241 236
242 public GarageModeServiceForTest(Context context, 237 GarageModeServiceForTest(Context context,
243 CarPowerManagementService powerManagementService, 238 CarPowerManagementService powerManagementService,
244 DeviceIdleControllerWrapper controllerWrapper) { 239 DeviceIdleControllerWrapper controllerWrapper) {
245 super(context, powerManagementService, controllerWrapper, Looper.myLooper()); 240 super(context, powerManagementService, controllerWrapper, Looper.myLooper());
diff --git a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
index 8ac7d9b1..b071e994 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
@@ -31,6 +31,7 @@ import android.car.hardware.CarSensorEvent;
31import android.car.hardware.CarSensorManager; 31import android.car.hardware.CarSensorManager;
32import android.car.hardware.property.CarPropertyEvent; 32import android.car.hardware.property.CarPropertyEvent;
33import android.car.hardware.property.ICarPropertyEventListener; 33import android.car.hardware.property.ICarPropertyEventListener;
34import android.car.user.CarUserManagerHelper;
34import android.content.Context; 35import android.content.Context;
35import android.content.Intent; 36import android.content.Intent;
36import android.content.IntentFilter; 37import android.content.IntentFilter;
@@ -72,7 +73,9 @@ import java.util.stream.Collectors;
72 * The following mocks are used: 73 * The following mocks are used:
73 * 1. {@link Context} provides files and a mocked {@link LocationManager}. 74 * 1. {@link Context} provides files and a mocked {@link LocationManager}.
74 * 2. {@link LocationManager} provides dummy {@link Location}s. 75 * 2. {@link LocationManager} provides dummy {@link Location}s.
75 * 3. {@link CarSensorService} registers a handler for sensor events and sends ignition-off events. 76 * 3. {@link CarPropertyService} registers a listener for ignition state events.
77 * 3. {@link CarPowerManagementService} registers a handler for power events.
78 * 4. {@link CarUserManagerHelper} tells whether or not the system user is headless.
76 */ 79 */
77@RunWith(AndroidJUnit4.class) 80@RunWith(AndroidJUnit4.class)
78public class CarLocationServiceTest { 81public class CarLocationServiceTest {
@@ -85,6 +88,7 @@ public class CarLocationServiceTest {
85 @Mock private LocationManager mMockLocationManager; 88 @Mock private LocationManager mMockLocationManager;
86 @Mock private CarPropertyService mMockCarPropertyService; 89 @Mock private CarPropertyService mMockCarPropertyService;
87 @Mock private CarPowerManagementService mMockCarPowerManagementService; 90 @Mock private CarPowerManagementService mMockCarPowerManagementService;
91 @Mock private CarUserManagerHelper mMockCarUserManagerHelper;
88 92
89 /** 93 /**
90 * Initialize all of the objects with the @Mock annotation. 94 * Initialize all of the objects with the @Mock annotation.
@@ -95,7 +99,7 @@ public class CarLocationServiceTest {
95 mContext = InstrumentationRegistry.getTargetContext(); 99 mContext = InstrumentationRegistry.getTargetContext();
96 mLatch = new CountDownLatch(1); 100 mLatch = new CountDownLatch(1);
97 mCarLocationService = new CarLocationService(mMockContext, mMockCarPowerManagementService, 101 mCarLocationService = new CarLocationService(mMockContext, mMockCarPowerManagementService,
98 mMockCarPropertyService) { 102 mMockCarPropertyService, mMockCarUserManagerHelper) {
99 @Override 103 @Override
100 void asyncOperation(Runnable operation) { 104 void asyncOperation(Runnable operation) {
101 super.asyncOperation(() -> { 105 super.asyncOperation(() -> {
@@ -143,12 +147,13 @@ public class CarLocationServiceTest {
143 mCarLocationService); 147 mCarLocationService);
144 verify(mMockContext).registerReceiver(eq(mCarLocationService), argument.capture()); 148 verify(mMockContext).registerReceiver(eq(mCarLocationService), argument.capture());
145 IntentFilter intentFilter = argument.getValue(); 149 IntentFilter intentFilter = argument.getValue();
146 assertEquals(3, intentFilter.countActions()); 150 assertEquals(4, intentFilter.countActions());
147 String[] actions = {intentFilter.getAction(0), intentFilter.getAction(1), 151 String[] actions = {intentFilter.getAction(0), intentFilter.getAction(1),
148 intentFilter.getAction(2)}; 152 intentFilter.getAction(2), intentFilter.getAction(3)};
149 assertTrue(ArrayUtils.contains(actions, Intent.ACTION_LOCKED_BOOT_COMPLETED)); 153 assertTrue(ArrayUtils.contains(actions, Intent.ACTION_LOCKED_BOOT_COMPLETED));
150 assertTrue(ArrayUtils.contains(actions, LocationManager.MODE_CHANGED_ACTION)); 154 assertTrue(ArrayUtils.contains(actions, LocationManager.MODE_CHANGED_ACTION));
151 assertTrue(ArrayUtils.contains(actions, LocationManager.GPS_ENABLED_CHANGE_ACTION)); 155 assertTrue(ArrayUtils.contains(actions, LocationManager.GPS_ENABLED_CHANGE_ACTION));
156 assertTrue(ArrayUtils.contains(actions, Intent.ACTION_USER_SWITCHED));
152 verify(mMockCarPropertyService).registerListener( 157 verify(mMockCarPropertyService).registerListener(
153 eq(CarSensorManager.SENSOR_TYPE_IGNITION_STATE), eq(0.0f), any()); 158 eq(CarSensorManager.SENSOR_TYPE_IGNITION_STATE), eq(0.0f), any());
154 } 159 }
@@ -166,10 +171,11 @@ public class CarLocationServiceTest {
166 171
167 /** 172 /**
168 * Test that the {@link CarLocationService} parses a location from a JSON serialization and then 173 * Test that the {@link CarLocationService} parses a location from a JSON serialization and then
169 * injects it into the {@link LocationManager} upon boot complete. 174 * injects it into the {@link LocationManager} upon boot complete if the system user is not
175 * headless.
170 */ 176 */
171 @Test 177 @Test
172 public void testLoadsLocation() throws IOException, InterruptedException { 178 public void testLoadsLocationOnLockedBootComplete() throws IOException, InterruptedException {
173 long currentTime = System.currentTimeMillis(); 179 long currentTime = System.currentTimeMillis();
174 long elapsedTime = SystemClock.elapsedRealtimeNanos(); 180 long elapsedTime = SystemClock.elapsedRealtimeNanos();
175 long pastTime = currentTime - 60000; 181 long pastTime = currentTime - 60000;
@@ -181,6 +187,7 @@ public class CarLocationServiceTest {
181 when(mMockLocationManager.injectLocation(argument.capture())).thenReturn(true); 187 when(mMockLocationManager.injectLocation(argument.capture())).thenReturn(true);
182 when(mMockContext.getFileStreamPath("location_cache.json")) 188 when(mMockContext.getFileStreamPath("location_cache.json"))
183 .thenReturn(mContext.getFileStreamPath(TEST_FILENAME)); 189 .thenReturn(mContext.getFileStreamPath(TEST_FILENAME));
190 when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(false);
184 191
185 mCarLocationService.onReceive(mMockContext, 192 mCarLocationService.onReceive(mMockContext,
186 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)); 193 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
@@ -196,6 +203,39 @@ public class CarLocationServiceTest {
196 } 203 }
197 204
198 /** 205 /**
206 * Test that the {@link CarLocationService} parses a location from a JSON seialization and then
207 * injects it into the {@link LocationManager} upon user switch if the system user is headless.
208 */
209 @Test
210 public void testLoadsLocationWithHeadlessSystemUser() throws IOException, InterruptedException {
211 long currentTime = System.currentTimeMillis();
212 long elapsedTime = SystemClock.elapsedRealtimeNanos();
213 long pastTime = currentTime - 60000;
214 writeCacheFile("{\"provider\": \"gps\", \"latitude\": 16.7666, \"longitude\": 3.0026,"
215 + "\"accuracy\":12.3, \"captureTime\": " + pastTime + "}");
216 ArgumentCaptor<Location> argument = ArgumentCaptor.forClass(Location.class);
217 when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
218 .thenReturn(mMockLocationManager);
219 when(mMockLocationManager.injectLocation(argument.capture())).thenReturn(true);
220 when(mMockContext.getFileStreamPath("location_cache.json"))
221 .thenReturn(mContext.getFileStreamPath(TEST_FILENAME));
222 when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
223
224 Intent userSwitchedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
225 userSwitchedIntent.putExtra(Intent.EXTRA_USER_HANDLE, 11);
226 mCarLocationService.onReceive(mMockContext, userSwitchedIntent);
227 mLatch.await();
228
229 Location location = argument.getValue();
230 assertEquals("gps", location.getProvider());
231 assertEquals(16.7666, location.getLatitude());
232 assertEquals(3.0026, location.getLongitude());
233 assertEquals(12.3f, location.getAccuracy());
234 assertTrue(location.getTime() >= currentTime);
235 assertTrue(location.getElapsedRealtimeNanos() >= elapsedTime);
236 }
237
238 /**
199 * Test that the {@link CarLocationService} does not inject a location if there is no location 239 * Test that the {@link CarLocationService} does not inject a location if there is no location
200 * cache file. 240 * cache file.
201 */ 241 */
diff --git a/tests/carservice_unit_test/src/com/android/car/CarUserManagerHelperTest.java b/tests/carservice_unit_test/src/com/android/car/CarUserManagerHelperTest.java
index 44cc0d6a..7f53da56 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarUserManagerHelperTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarUserManagerHelperTest.java
@@ -18,8 +18,12 @@ package com.android.car;
18 18
19import static com.google.common.truth.Truth.assertThat; 19import static com.google.common.truth.Truth.assertThat;
20 20
21import static org.mockito.ArgumentMatchers.anyInt;
22import static org.mockito.Matchers.any;
23import static org.mockito.Mockito.doReturn;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.times;
21import static org.mockito.Mockito.verify; 26import static org.mockito.Mockito.verify;
22import static org.mockito.Mockito.when;
23 27
24import android.app.ActivityManager; 28import android.app.ActivityManager;
25import android.car.user.CarUserManagerHelper; 29import android.car.user.CarUserManagerHelper;
@@ -30,11 +34,13 @@ import android.content.IntentFilter;
30import android.content.pm.UserInfo; 34import android.content.pm.UserInfo;
31import android.graphics.Bitmap; 35import android.graphics.Bitmap;
32import android.graphics.drawable.Drawable; 36import android.graphics.drawable.Drawable;
37import android.os.Bundle;
33import android.os.Handler; 38import android.os.Handler;
34import android.os.SystemProperties; 39import android.os.SystemProperties;
35import android.os.UserHandle; 40import android.os.UserHandle;
36import android.os.UserManager; 41import android.os.UserManager;
37import android.support.test.InstrumentationRegistry; 42import android.support.test.InstrumentationRegistry;
43import android.support.test.filters.SmallTest;
38import android.support.test.runner.AndroidJUnit4; 44import android.support.test.runner.AndroidJUnit4;
39 45
40import org.junit.Before; 46import org.junit.Before;
@@ -42,6 +48,7 @@ import org.junit.Test;
42import org.junit.runner.RunWith; 48import org.junit.runner.RunWith;
43import org.mockito.ArgumentCaptor; 49import org.mockito.ArgumentCaptor;
44import org.mockito.Mock; 50import org.mockito.Mock;
51import org.mockito.Mockito;
45import org.mockito.MockitoAnnotations; 52import org.mockito.MockitoAnnotations;
46 53
47import java.util.ArrayList; 54import java.util.ArrayList;
@@ -54,10 +61,11 @@ import java.util.List;
54 * The following mocks are used: 61 * The following mocks are used:
55 * 1. {@link Context} provides system services and resources. 62 * 1. {@link Context} provides system services and resources.
56 * 2. {@link UserManager} provides dummy users and user info. 63 * 2. {@link UserManager} provides dummy users and user info.
57 * 3. {@link ActivityManager} provides dummy current process user. 64 * 3. {@link ActivityManager} to verify user switch is invoked.
58 * 4. {@link CarUserManagerHelper.OnUsersUpdateListener} registers a listener for user updates. 65 * 4. {@link CarUserManagerHelper.OnUsersUpdateListener} registers a listener for user updates.
59 */ 66 */
60@RunWith(AndroidJUnit4.class) 67@RunWith(AndroidJUnit4.class)
68@SmallTest
61public class CarUserManagerHelperTest { 69public class CarUserManagerHelperTest {
62 @Mock 70 @Mock
63 private Context mContext; 71 private Context mContext;
@@ -68,25 +76,37 @@ public class CarUserManagerHelperTest {
68 @Mock 76 @Mock
69 private CarUserManagerHelper.OnUsersUpdateListener mTestListener; 77 private CarUserManagerHelper.OnUsersUpdateListener mTestListener;
70 78
71 private CarUserManagerHelper mHelper; 79 private CarUserManagerHelper mCarUserManagerHelper;
72 private UserInfo mCurrentProcessUser; 80 private UserInfo mCurrentProcessUser;
73 private UserInfo mSystemUser; 81 private UserInfo mSystemUser;
74 private String mGuestUserName = "testGuest"; 82 private String mGuestUserName = "testGuest";
75 private String mTestUserName = "testUser"; 83 private String mTestUserName = "testUser";
84 private int mForegroundUserId;
85 private UserInfo mForegroundUser;
76 86
77 @Before 87 @Before
78 public void setUpMocksAndVariables() throws Exception { 88 public void setUpMocksAndVariables() throws Exception {
79 MockitoAnnotations.initMocks(this); 89 MockitoAnnotations.initMocks(this);
80 when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); 90 doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
81 when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager); 91 doReturn(mActivityManager).when(mContext).getSystemService(Context.ACTIVITY_SERVICE);
82 when(mContext.getResources()) 92 doReturn(InstrumentationRegistry.getTargetContext().getResources())
83 .thenReturn(InstrumentationRegistry.getTargetContext().getResources()); 93 .when(mContext).getResources();
84 when(mContext.getApplicationContext()).thenReturn(mContext); 94 doReturn(mContext).when(mContext).getApplicationContext();
85 mHelper = new CarUserManagerHelper(mContext); 95 mCarUserManagerHelper = new CarUserManagerHelper(mContext);
86 96
87 mCurrentProcessUser = createUserInfoForId(UserHandle.myUserId()); 97 mCurrentProcessUser = createUserInfoForId(UserHandle.myUserId());
88 mSystemUser = createUserInfoForId(UserHandle.USER_SYSTEM); 98 mSystemUser = createUserInfoForId(UserHandle.USER_SYSTEM);
89 when(mUserManager.getUserInfo(UserHandle.myUserId())).thenReturn(mCurrentProcessUser); 99 doReturn(mCurrentProcessUser).when(mUserManager).getUserInfo(UserHandle.myUserId());
100
101 // Get the ID of the foreground user running this test.
102 // We cannot mock the foreground user since getCurrentUser is static.
103 // We cannot rely on foreground_id != system_id, they could be the same user.
104 mForegroundUserId = ActivityManager.getCurrentUser();
105 mForegroundUser = createUserInfoForId(mForegroundUserId);
106
107 // Restore the non-headless state before every test. Individual tests can set the property
108 // to true to test the headless system user scenario.
109 SystemProperties.set("android.car.systemuser.headless", "false");
90 } 110 }
91 111
92 @Test 112 @Test
@@ -94,10 +114,10 @@ public class CarUserManagerHelperTest {
94 UserInfo testInfo = new UserInfo(); 114 UserInfo testInfo = new UserInfo();
95 115
96 testInfo.id = UserHandle.USER_SYSTEM; 116 testInfo.id = UserHandle.USER_SYSTEM;
97 assertThat(mHelper.isSystemUser(testInfo)).isTrue(); 117 assertThat(mCarUserManagerHelper.isSystemUser(testInfo)).isTrue();
98 118
99 testInfo.id = UserHandle.USER_SYSTEM + 2; // Make it different than system id. 119 testInfo.id = UserHandle.USER_SYSTEM + 2; // Make it different than system id.
100 assertThat(mHelper.isSystemUser(testInfo)).isFalse(); 120 assertThat(mCarUserManagerHelper.isSystemUser(testInfo)).isFalse();
101 } 121 }
102 122
103 // System user will not be returned when calling get all users. 123 // System user will not be returned when calling get all users.
@@ -108,92 +128,67 @@ public class CarUserManagerHelperTest {
108 UserInfo otherUser2 = createUserInfoForId(11); 128 UserInfo otherUser2 = createUserInfoForId(11);
109 UserInfo otherUser3 = createUserInfoForId(12); 129 UserInfo otherUser3 = createUserInfoForId(12);
110 130
111 List<UserInfo> testUsers = new ArrayList<>(); 131 mockGetUsers(mSystemUser, otherUser1, otherUser2, otherUser3);
112 testUsers.add(mSystemUser);
113 testUsers.add(otherUser1);
114 testUsers.add(otherUser2);
115 testUsers.add(otherUser3);
116 132
117 when(mUserManager.getUsers(true)).thenReturn(testUsers); 133 assertThat(mCarUserManagerHelper.getAllUsers())
118 134 .containsExactly(otherUser1, otherUser2, otherUser3);
119 // Should return 3 users that don't have SYSTEM USER id.
120 assertThat(mHelper.getAllUsers()).hasSize(3);
121 assertThat(mHelper.getAllUsers())
122 .containsExactly(otherUser1, otherUser2, otherUser3);
123 } 135 }
124 136
125 @Test 137 @Test
126 public void testHeadlessUser0GetAllUsersWithActiveForegroundUser_NotReturnSystemUser() { 138 public void testGetAllSwitchableUsers() {
127 SystemProperties.set("android.car.systemuser.headless", "true"); 139 // Create two non-foreground users.
128 mCurrentProcessUser = createUserInfoForId(10); 140 UserInfo user1 = createUserInfoForId(mForegroundUserId + 1);
129 141 UserInfo user2 = createUserInfoForId(mForegroundUserId + 2);
130 UserInfo otherUser1 = createUserInfoForId(11);
131 UserInfo otherUser2 = createUserInfoForId(12);
132 UserInfo otherUser3 = createUserInfoForId(13);
133 142
134 List<UserInfo> testUsers = new ArrayList<>(); 143 mockGetUsers(mForegroundUser, user1, user2);
135 testUsers.add(mSystemUser);
136 testUsers.add(mCurrentProcessUser);
137 testUsers.add(otherUser1);
138 testUsers.add(otherUser2);
139 testUsers.add(otherUser3);
140 144
141 when(mUserManager.getUsers(true)).thenReturn(testUsers); 145 // Should return all non-foreground users.
146 assertThat(mCarUserManagerHelper.getAllSwitchableUsers()).containsExactly(user1, user2);
147 }
142 148
143 assertThat(mHelper.getAllUsers().size()).isEqualTo(4); 149 @Test
144 assertThat(mHelper.getAllUsers()) 150 public void testGetAllPersistentUsers() {
145 .containsExactly(mCurrentProcessUser, otherUser1, otherUser2, otherUser3); 151 // Create two non-ephemeral users.
152 UserInfo user1 = createUserInfoForId(mForegroundUserId);
153 UserInfo user2 = createUserInfoForId(mForegroundUserId + 1);
154 // Create two ephemeral users.
155 UserInfo user3 = new UserInfo(
156 /* id= */mForegroundUserId + 2, /* name = */ "user3", UserInfo.FLAG_EPHEMERAL);
157 UserInfo user4 = new UserInfo(
158 /* id= */mForegroundUserId + 3, /* name = */ "user4", UserInfo.FLAG_EPHEMERAL);
159
160 mockGetUsers(user1, user2, user3, user4);
161
162 // Should return all non-ephemeral users.
163 assertThat(mCarUserManagerHelper.getAllPersistentUsers()).containsExactly(user1, user2);
146 } 164 }
147 165
148 @Test 166 @Test
149 public void testGetAllSwitchableUsers() { 167 public void testGetAllAdminUsers() {
150 UserInfo user1 = createUserInfoForId(10); 168 // Create two admin, and two non-admin users.
169 UserInfo user1 = new UserInfo(/* id= */ 10, /* name = */ "user10", UserInfo.FLAG_ADMIN);
151 UserInfo user2 = createUserInfoForId(11); 170 UserInfo user2 = createUserInfoForId(11);
152 UserInfo user3 = createUserInfoForId(12); 171 UserInfo user3 = createUserInfoForId(12);
172 UserInfo user4 = new UserInfo(/* id= */ 13, /* name = */ "user13", UserInfo.FLAG_ADMIN);
153 173
154 List<UserInfo> testUsers = new ArrayList<>(); 174 mockGetUsers(user1, user2, user3, user4);
155 testUsers.add(mSystemUser);
156 testUsers.add(user1);
157 testUsers.add(user2);
158 testUsers.add(user3);
159
160 when(mUserManager.getUsers(true)).thenReturn(new ArrayList<>(testUsers));
161
162 // Should return all 3 non-system users.
163 assertThat(mHelper.getAllUsers().size())
164 .isEqualTo(3);
165 175
166 when(mUserManager.getUserInfo(UserHandle.myUserId())).thenReturn(user1); 176 // Should return only admin users.
167 // Should return user 10, 11 and 12. 177 assertThat(mCarUserManagerHelper.getAllAdminUsers()).containsExactly(user1, user4);
168 assertThat(mHelper.getAllSwitchableUsers().size())
169 .isEqualTo(3);
170 assertThat(mHelper.getAllSwitchableUsers()).contains(user1);
171 assertThat(mHelper.getAllSwitchableUsers()).contains(user2);
172 assertThat(mHelper.getAllSwitchableUsers()).contains(user3);
173 } 178 }
174 179
175 // Get all users for headless user 0 model should exclude system user by default.
176 @Test 180 @Test
177 public void testHeadlessUser0GetAllSwitchableUsers() { 181 public void testGetAllUsersExceptGuests() {
178 SystemProperties.set("android.car.systemuser.headless", "true"); 182 // Create two users and a guest user.
179 UserInfo user1 = createUserInfoForId(10); 183 UserInfo user1 = createUserInfoForId(10);
180 UserInfo user2 = createUserInfoForId(11); 184 UserInfo user2 = createUserInfoForId(12);
181 UserInfo user3 = createUserInfoForId(12); 185 UserInfo user3 = new UserInfo(/* id= */ 13, /* name = */ "user13", UserInfo.FLAG_GUEST);
182
183 List<UserInfo> testUsers = new ArrayList<>();
184 testUsers.add(mSystemUser);
185 testUsers.add(user1);
186 testUsers.add(user2);
187 testUsers.add(user3);
188 186
189 when(mUserManager.getUsers(true)).thenReturn(new ArrayList<>(testUsers)); 187 mockGetUsers(user1, user2, user3);
190 188
191 // Should return all 3 non-system users. 189 // Should not return guests.
192 assertThat(mHelper.getAllUsers()).hasSize(3); 190 assertThat(mCarUserManagerHelper.getAllUsersExceptGuests())
193 191 .containsExactly(user1, user2);
194 when(mUserManager.getUserInfo(UserHandle.myUserId())).thenReturn(user1);
195 // Should return user 10, 11 and 12.
196 assertThat(mHelper.getAllSwitchableUsers()).containsExactly(user1, user2, user3);
197 } 192 }
198 193
199 @Test 194 @Test
@@ -202,127 +197,304 @@ public class CarUserManagerHelperTest {
202 197
203 // System user cannot be removed. 198 // System user cannot be removed.
204 testInfo.id = UserHandle.USER_SYSTEM; 199 testInfo.id = UserHandle.USER_SYSTEM;
205 assertThat(mHelper.canUserBeRemoved(testInfo)).isFalse(); 200 assertThat(mCarUserManagerHelper.canUserBeRemoved(testInfo)).isFalse();
206 201
207 testInfo.id = UserHandle.USER_SYSTEM + 2; // Make it different than system id. 202 testInfo.id = UserHandle.USER_SYSTEM + 2; // Make it different than system id.
208 assertThat(mHelper.canUserBeRemoved(testInfo)).isTrue(); 203 assertThat(mCarUserManagerHelper.canUserBeRemoved(testInfo)).isTrue();
209 } 204 }
210 205
211 @Test 206 @Test
212 public void testCurrentProcessCanAddUsers() { 207 public void testCurrentProcessCanAddUsers() {
213 when(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).thenReturn(false); 208 doReturn(false).when(mUserManager)
214 assertThat(mHelper.canCurrentProcessAddUsers()).isTrue(); 209 .hasUserRestriction(UserManager.DISALLOW_ADD_USER);
210 assertThat(mCarUserManagerHelper.canCurrentProcessAddUsers()).isTrue();
215 211
216 when(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).thenReturn(true); 212 doReturn(true).when(mUserManager)
217 assertThat(mHelper.canCurrentProcessAddUsers()).isFalse(); 213 .hasUserRestriction(UserManager.DISALLOW_ADD_USER);
214 assertThat(mCarUserManagerHelper.canCurrentProcessAddUsers()).isFalse();
218 } 215 }
219 216
220 @Test 217 @Test
221 public void testCurrentProcessCanRemoveUsers() { 218 public void testCurrentProcessCanRemoveUsers() {
222 when(mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)).thenReturn(false); 219 doReturn(false).when(mUserManager)
223 assertThat(mHelper.canCurrentProcessRemoveUsers()).isTrue(); 220 .hasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
221 assertThat(mCarUserManagerHelper.canCurrentProcessRemoveUsers()).isTrue();
224 222
225 when(mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)).thenReturn(true); 223 doReturn(true).when(mUserManager)
226 assertThat(mHelper.canCurrentProcessRemoveUsers()).isFalse(); 224 .hasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
225 assertThat(mCarUserManagerHelper.canCurrentProcessRemoveUsers()).isFalse();
227 } 226 }
228 227
229 @Test 228 @Test
230 public void testCurrentProcessCanSwitchUsers() { 229 public void testCurrentProcessCanSwitchUsers() {
231 when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(false); 230 doReturn(false).when(mUserManager)
232 assertThat(mHelper.canCurrentProcessSwitchUsers()).isTrue(); 231 .hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
232 assertThat(mCarUserManagerHelper.canCurrentProcessSwitchUsers()).isTrue();
233 233
234 when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(true); 234 doReturn(true).when(mUserManager)
235 assertThat(mHelper.canCurrentProcessSwitchUsers()).isFalse(); 235 .hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
236 assertThat(mCarUserManagerHelper.canCurrentProcessSwitchUsers()).isFalse();
236 } 237 }
237 238
238 @Test 239 @Test
239 public void testCurrentGuestProcessCannotModifyAccounts() { 240 public void testCurrentGuestProcessCannotModifyAccounts() {
240 assertThat(mHelper.canCurrentProcessModifyAccounts()).isTrue(); 241 assertThat(mCarUserManagerHelper.canCurrentProcessModifyAccounts()).isTrue();
242
243 doReturn(true).when(mUserManager).isGuestUser();
241 244
242 when(mUserManager.isGuestUser()).thenReturn(true); 245 assertThat(mCarUserManagerHelper.canCurrentProcessModifyAccounts()).isFalse();
243 assertThat(mHelper.canCurrentProcessModifyAccounts()).isFalse();
244 } 246 }
245 247
246 @Test 248 @Test
247 public void testCurrentDemoProcessCannotModifyAccounts() { 249 public void testCurrentDemoProcessCannotModifyAccounts() {
248 assertThat(mHelper.canCurrentProcessModifyAccounts()).isTrue(); 250 assertThat(mCarUserManagerHelper.canCurrentProcessModifyAccounts()).isTrue();
251
252 doReturn(true).when(mUserManager).isDemoUser();
249 253
250 when(mUserManager.isDemoUser()).thenReturn(true); 254 assertThat(mCarUserManagerHelper.canCurrentProcessModifyAccounts()).isFalse();
251 assertThat(mHelper.canCurrentProcessModifyAccounts()).isFalse();
252 } 255 }
253 256
254 @Test 257 @Test
255 public void testCurrentDisallowModifyAccountsProcessIsEnforced() { 258 public void testCurrentDisallowModifyAccountsProcessIsEnforced() {
256 assertThat(mHelper.canCurrentProcessModifyAccounts()).isTrue(); 259 assertThat(mCarUserManagerHelper.canCurrentProcessModifyAccounts()).isTrue();
260
261 doReturn(true).when(mUserManager)
262 .hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS);
263
264 assertThat(mCarUserManagerHelper.canCurrentProcessModifyAccounts()).isFalse();
265 }
266
267 @Test
268 public void testGetMaxSupportedUsers() {
269 SystemProperties.set("fw.max_users", "11");
270
271 assertThat(mCarUserManagerHelper.getMaxSupportedUsers()).isEqualTo(11);
272
273 // In headless user 0 model, we want to exclude the system user.
274 SystemProperties.set("android.car.systemuser.headless", "true");
275 assertThat(mCarUserManagerHelper.getMaxSupportedUsers()).isEqualTo(10);
276 }
277
278 @Test
279 public void testGetMaxSupportedRealUsers() {
280 SystemProperties.set("fw.max_users", "7");
281
282 // Create three managed profiles, and two normal users.
283 UserInfo user1 = createUserInfoForId(10);
284 UserInfo user2 =
285 new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
286 UserInfo user3 =
287 new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
288 UserInfo user4 = createUserInfoForId(13);
289 UserInfo user5 =
290 new UserInfo(/* id= */ 14, /* name = */ "user14", UserInfo.FLAG_MANAGED_PROFILE);
291
292 mockGetUsers(user1, user2, user3, user4, user5);
293
294 // Max users - # managed profiles.
295 assertThat(mCarUserManagerHelper.getMaxSupportedRealUsers()).isEqualTo(4);
296 }
297
298 @Test
299 public void testIsUserLimitReached() {
300 UserInfo user1 = createUserInfoForId(10);
301 UserInfo user2 =
302 new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
303 UserInfo user3 =
304 new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
305 UserInfo user4 = createUserInfoForId(13);
257 306
258 when(mUserManager.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) 307 mockGetUsers(user1, user2, user3, user4);
259 .thenReturn(true); 308
260 assertThat(mHelper.canCurrentProcessModifyAccounts()).isFalse(); 309 SystemProperties.set("fw.max_users", "5");
310 assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
311
312 SystemProperties.set("fw.max_users", "4");
313 assertThat(mCarUserManagerHelper.isUserLimitReached()).isTrue();
314 }
315
316 @Test
317 public void testHeadlessSystemUser_IsUserLimitReached() {
318 SystemProperties.set("android.car.systemuser.headless", "true");
319 UserInfo user1 = createUserInfoForId(10);
320 UserInfo user2 =
321 new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
322 UserInfo user3 =
323 new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
324 UserInfo user4 = createUserInfoForId(13);
325
326 mockGetUsers(mSystemUser, user1, user2, user3, user4);
327
328 SystemProperties.set("fw.max_users", "6");
329 assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
330
331 SystemProperties.set("fw.max_users", "5");
332 assertThat(mCarUserManagerHelper.isUserLimitReached()).isTrue();
333 }
334
335 @Test
336 public void testIsUserLimitReachedIgnoresGuests() {
337 SystemProperties.set("fw.max_users", "5");
338
339 UserInfo user1 = createUserInfoForId(10);
340 UserInfo user2 =
341 new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
342 UserInfo user3 =
343 new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
344 UserInfo user4 = createUserInfoForId(13);
345 UserInfo user5 = new UserInfo(/* id= */ 14, /* name = */ "user14", UserInfo.FLAG_GUEST);
346 UserInfo user6 = createUserInfoForId(15);
347
348 mockGetUsers(user1, user2, user3, user4);
349 assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
350
351 // Add guest user. Verify it doesn't affect the limit.
352 mockGetUsers(user1, user2, user3, user4, user5);
353 assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
354
355 // Add normal user. Limit is reached
356 mockGetUsers(user1, user2, user3, user4, user5, user6);
357 assertThat(mCarUserManagerHelper.isUserLimitReached()).isTrue();
261 } 358 }
262 359
263 @Test 360 @Test
264 public void testCreateNewAdminUser() { 361 public void testCreateNewAdminUser() {
362 // Make sure current user is admin, since only admins can create other admins.
363 doReturn(true).when(mUserManager).isAdminUser();
364
265 // Verify createUser on UserManager gets called. 365 // Verify createUser on UserManager gets called.
266 mHelper.createNewAdminUser(mTestUserName); 366 mCarUserManagerHelper.createNewAdminUser(mTestUserName);
267 verify(mUserManager).createUser(mTestUserName, UserInfo.FLAG_ADMIN); 367 verify(mUserManager).createUser(mTestUserName, UserInfo.FLAG_ADMIN);
268 368
269 when(mUserManager.createUser(mTestUserName, UserInfo.FLAG_ADMIN)).thenReturn(null); 369 doReturn(null).when(mUserManager).createUser(mTestUserName, UserInfo.FLAG_ADMIN);
270 assertThat(mHelper.createNewAdminUser(mTestUserName)).isNull(); 370 assertThat(mCarUserManagerHelper.createNewAdminUser(mTestUserName)).isNull();
271 371
272 UserInfo newUser = new UserInfo(); 372 UserInfo newUser = new UserInfo();
273 newUser.name = mTestUserName; 373 newUser.name = mTestUserName;
274 when(mUserManager.createUser(mTestUserName, UserInfo.FLAG_ADMIN)).thenReturn(newUser); 374 doReturn(newUser).when(mUserManager).createUser(mTestUserName, UserInfo.FLAG_ADMIN);
275 assertThat(mHelper.createNewAdminUser(mTestUserName)).isEqualTo(newUser); 375 assertThat(mCarUserManagerHelper.createNewAdminUser(mTestUserName)).isEqualTo(newUser);
376 }
377
378 @Test
379 public void testAdminsCanCreateAdmins() {
380 String newAdminName = "Test new admin";
381 UserInfo expectedAdmin = new UserInfo();
382 expectedAdmin.name = newAdminName;
383 doReturn(expectedAdmin).when(mUserManager).createUser(newAdminName, UserInfo.FLAG_ADMIN);
384
385 // Admins can create other admins.
386 doReturn(true).when(mUserManager).isAdminUser();
387 UserInfo actualAdmin = mCarUserManagerHelper.createNewAdminUser(newAdminName);
388 assertThat(actualAdmin).isEqualTo(expectedAdmin);
389 }
390
391 @Test
392 public void testNonAdminsCanNotCreateAdmins() {
393 String newAdminName = "Test new admin";
394 UserInfo expectedAdmin = new UserInfo();
395 expectedAdmin.name = newAdminName;
396 doReturn(expectedAdmin).when(mUserManager).createUser(newAdminName, UserInfo.FLAG_ADMIN);
397
398 // Test that non-admins cannot create new admins.
399 doReturn(false).when(mUserManager).isAdminUser(); // Current user non-admin.
400 assertThat(mCarUserManagerHelper.createNewAdminUser(newAdminName)).isNull();
401 }
402
403 @Test
404 public void testSystemUserCanCreateAdmins() {
405 String newAdminName = "Test new admin";
406 UserInfo expectedAdmin = new UserInfo();
407 expectedAdmin.name = newAdminName;
408
409 doReturn(expectedAdmin).when(mUserManager).createUser(newAdminName, UserInfo.FLAG_ADMIN);
410
411 // System user can create admins.
412 doReturn(true).when(mUserManager).isSystemUser();
413 UserInfo actualAdmin = mCarUserManagerHelper.createNewAdminUser(newAdminName);
414 assertThat(actualAdmin).isEqualTo(expectedAdmin);
276 } 415 }
277 416
278 @Test 417 @Test
279 public void testCreateNewNonAdminUser() { 418 public void testCreateNewNonAdminUser() {
280 // Verify createUser on UserManager gets called. 419 // Verify createUser on UserManager gets called.
281 mHelper.createNewNonAdminUser(mTestUserName); 420 mCarUserManagerHelper.createNewNonAdminUser(mTestUserName);
282 verify(mUserManager).createUser(mTestUserName, 0); 421 verify(mUserManager).createUser(mTestUserName, 0);
283 422
284 when(mUserManager.createUser(mTestUserName, 0)).thenReturn(null); 423 doReturn(null).when(mUserManager).createUser(mTestUserName, 0);
285 assertThat(mHelper.createNewNonAdminUser(mTestUserName)).isNull(); 424 assertThat(mCarUserManagerHelper.createNewNonAdminUser(mTestUserName)).isNull();
286 425
287 UserInfo newUser = new UserInfo(); 426 UserInfo newUser = new UserInfo();
288 newUser.name = mTestUserName; 427 newUser.name = mTestUserName;
289 when(mUserManager.createUser(mTestUserName, 0)).thenReturn(newUser); 428 doReturn(newUser).when(mUserManager).createUser(mTestUserName, 0);
290 assertThat(mHelper.createNewNonAdminUser(mTestUserName)).isEqualTo(newUser); 429 assertThat(mCarUserManagerHelper.createNewNonAdminUser(mTestUserName)).isEqualTo(newUser);
430 }
431
432 @Test
433 public void testCannotRemoveSystemUser() {
434 assertThat(mCarUserManagerHelper.removeUser(mSystemUser, mGuestUserName)).isFalse();
435 }
436
437 @Test
438 public void testAdminsCanRemoveOtherUsers() {
439 int idToRemove = mCurrentProcessUser.id + 2;
440 UserInfo userToRemove = createUserInfoForId(idToRemove);
441
442 doReturn(true).when(mUserManager).removeUser(idToRemove);
443
444 // If Admin is removing non-current, non-system user, simply calls removeUser.
445 doReturn(true).when(mUserManager).isAdminUser();
446 assertThat(mCarUserManagerHelper.removeUser(userToRemove, mGuestUserName)).isTrue();
447 verify(mUserManager).removeUser(idToRemove);
448 }
449
450 @Test
451 public void testNonAdminsCanNotRemoveOtherUsers() {
452 UserInfo otherUser = createUserInfoForId(mCurrentProcessUser.id + 2);
453
454 // Make current user non-admin.
455 doReturn(false).when(mUserManager).isAdminUser();
456
457 // Mock so that removeUser always pretends it's successful.
458 doReturn(true).when(mUserManager).removeUser(anyInt());
459
460 // If Non-Admin is trying to remove someone other than themselves, they should fail.
461 assertThat(mCarUserManagerHelper.removeUser(otherUser, mGuestUserName)).isFalse();
462 verify(mUserManager, never()).removeUser(otherUser.id);
291 } 463 }
292 464
293 @Test 465 @Test
294 public void testRemoveUser() { 466 public void testRemoveLastActiveUser() {
295 // Cannot remove system user. 467 // Cannot remove system user.
296 assertThat(mHelper.removeUser(mSystemUser, mGuestUserName)).isFalse(); 468 assertThat(mCarUserManagerHelper.removeUser(mSystemUser, mGuestUserName)).isFalse();
297 469
298 // Removing non-current, non-system user, simply calls removeUser. 470 UserInfo adminInfo = new UserInfo(/* id= */10, "admin", UserInfo.FLAG_ADMIN);
299 UserInfo userToRemove = createUserInfoForId(mCurrentProcessUser.id + 2); 471 mockGetUsers(adminInfo);
300 472
301 mHelper.removeUser(userToRemove, mGuestUserName); 473 assertThat(mCarUserManagerHelper.removeUser(adminInfo, mGuestUserName))
302 verify(mUserManager).removeUser(mCurrentProcessUser.id + 2); 474 .isEqualTo(false);
303 } 475 }
304 476
305 @Test 477 @Test
306 public void testSwitchToGuest() { 478 public void testSwitchToGuest() {
307 mHelper.startNewGuestSession(mGuestUserName); 479 mCarUserManagerHelper.startNewGuestSession(mGuestUserName);
308 verify(mUserManager).createGuest(mContext, mGuestUserName); 480 verify(mUserManager).createGuest(mContext, mGuestUserName);
309 481
310 UserInfo guestInfo = new UserInfo(21, mGuestUserName, UserInfo.FLAG_GUEST); 482 UserInfo guestInfo = new UserInfo(/* id= */21, mGuestUserName, UserInfo.FLAG_GUEST);
311 when(mUserManager.createGuest(mContext, mGuestUserName)).thenReturn(guestInfo); 483 doReturn(guestInfo).when(mUserManager).createGuest(mContext, mGuestUserName);
312 mHelper.startNewGuestSession(mGuestUserName); 484 mCarUserManagerHelper.startNewGuestSession(mGuestUserName);
313 verify(mActivityManager).switchUser(21); 485 verify(mActivityManager).switchUser(21);
314 } 486 }
315 487
316 @Test 488 @Test
317 public void testGetUserIcon() { 489 public void testGetUserIcon() {
318 mHelper.getUserIcon(mCurrentProcessUser); 490 mCarUserManagerHelper.getUserIcon(mCurrentProcessUser);
319 verify(mUserManager).getUserIcon(mCurrentProcessUser.id); 491 verify(mUserManager).getUserIcon(mCurrentProcessUser.id);
320 } 492 }
321 493
322 @Test 494 @Test
323 public void testScaleUserIcon() { 495 public void testScaleUserIcon() {
324 Bitmap fakeIcon = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 496 Bitmap fakeIcon = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
325 Drawable scaledIcon = mHelper.scaleUserIcon(fakeIcon, 300); 497 Drawable scaledIcon = mCarUserManagerHelper.scaleUserIcon(fakeIcon, 300);
326 assertThat(scaledIcon.getIntrinsicWidth()).isEqualTo(300); 498 assertThat(scaledIcon.getIntrinsicWidth()).isEqualTo(300);
327 assertThat(scaledIcon.getIntrinsicHeight()).isEqualTo(300); 499 assertThat(scaledIcon.getIntrinsicHeight()).isEqualTo(300);
328 } 500 }
@@ -331,13 +503,107 @@ public class CarUserManagerHelperTest {
331 public void testSetUserName() { 503 public void testSetUserName() {
332 UserInfo testInfo = createUserInfoForId(mCurrentProcessUser.id + 3); 504 UserInfo testInfo = createUserInfoForId(mCurrentProcessUser.id + 3);
333 String newName = "New Test Name"; 505 String newName = "New Test Name";
334 mHelper.setUserName(testInfo, newName); 506 mCarUserManagerHelper.setUserName(testInfo, newName);
335 verify(mUserManager).setUserName(mCurrentProcessUser.id + 3, newName); 507 verify(mUserManager).setUserName(mCurrentProcessUser.id + 3, newName);
336 } 508 }
337 509
338 @Test 510 @Test
511 public void testIsCurrentProcessSystemUser() {
512 doReturn(true).when(mUserManager).isAdminUser();
513 assertThat(mCarUserManagerHelper.isCurrentProcessAdminUser()).isTrue();
514
515 doReturn(false).when(mUserManager).isAdminUser();
516 assertThat(mCarUserManagerHelper.isCurrentProcessAdminUser()).isFalse();
517 }
518
519 @Test
520 public void testAssignAdminPrivileges() {
521 int userId = 30;
522 UserInfo testInfo = createUserInfoForId(userId);
523
524 // Test that non-admins cannot assign admin privileges.
525 doReturn(false).when(mUserManager).isAdminUser(); // Current user non-admin.
526 mCarUserManagerHelper.assignAdminPrivileges(testInfo);
527 verify(mUserManager, never()).setUserAdmin(userId);
528
529 // Admins can assign admin privileges.
530 doReturn(true).when(mUserManager).isAdminUser();
531 mCarUserManagerHelper.assignAdminPrivileges(testInfo);
532 verify(mUserManager).setUserAdmin(userId);
533 }
534
535 @Test
536 public void testSetUserRestriction() {
537 int userId = 20;
538 UserInfo testInfo = createUserInfoForId(userId);
539
540 mCarUserManagerHelper.setUserRestriction(
541 testInfo, UserManager.DISALLOW_ADD_USER, /* enable= */ true);
542 verify(mUserManager).setUserRestriction(
543 UserManager.DISALLOW_ADD_USER, true, UserHandle.of(userId));
544
545 mCarUserManagerHelper.setUserRestriction(
546 testInfo, UserManager.DISALLOW_REMOVE_USER, /* enable= */ false);
547 verify(mUserManager).setUserRestriction(
548 UserManager.DISALLOW_REMOVE_USER, false, UserHandle.of(userId));
549 }
550
551 @Test
552 public void testDefaultNonAdminRestrictions() {
553 String testUserName = "Test User";
554 int userId = 20;
555 UserInfo newNonAdmin = createUserInfoForId(userId);
556
557 doReturn(newNonAdmin).when(mUserManager).createUser(testUserName, /* flags= */ 0);
558
559 mCarUserManagerHelper.createNewNonAdminUser(testUserName);
560
561 verify(mUserManager).setUserRestriction(
562 UserManager.DISALLOW_FACTORY_RESET, /* enable= */ true, UserHandle.of(userId));
563 verify(mUserManager).setUserRestriction(
564 UserManager.DISALLOW_SMS, /* enable= */ false, UserHandle.of(userId));
565 verify(mUserManager).setUserRestriction(
566 UserManager.DISALLOW_OUTGOING_CALLS, /* enable= */ false, UserHandle.of(userId));
567 }
568
569 @Test
570 public void testDefaultGuestRestrictions() {
571 int guestRestrictionsExpectedCount = 7;
572
573 ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
574 mCarUserManagerHelper.initDefaultGuestRestrictions();
575
576 verify(mUserManager).setDefaultGuestRestrictions(bundleCaptor.capture());
577 Bundle guestRestrictions = bundleCaptor.getValue();
578
579 assertThat(guestRestrictions.keySet()).hasSize(guestRestrictionsExpectedCount);
580 assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_FACTORY_RESET)).isTrue();
581 assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_REMOVE_USER)).isTrue();
582 assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)).isTrue();
583 assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)).isTrue();
584 assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_SMS)).isTrue();
585 assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_APPS)).isTrue();
586 assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS)).isTrue();
587 }
588
589 @Test
590 public void testAssigningAdminPrivilegesRemovesNonAdminRestrictions() {
591 int testUserId = 30;
592 boolean restrictionEnabled = false;
593 UserInfo testInfo = createUserInfoForId(testUserId);
594
595 // Only admins can assign privileges.
596 doReturn(true).when(mUserManager).isAdminUser();
597
598 mCarUserManagerHelper.assignAdminPrivileges(testInfo);
599
600 verify(mUserManager).setUserRestriction(
601 UserManager.DISALLOW_FACTORY_RESET, restrictionEnabled, UserHandle.of(testUserId));
602 }
603
604 @Test
339 public void testRegisterUserChangeReceiver() { 605 public void testRegisterUserChangeReceiver() {
340 mHelper.registerOnUsersUpdateListener(mTestListener); 606 mCarUserManagerHelper.registerOnUsersUpdateListener(mTestListener);
341 607
342 ArgumentCaptor<BroadcastReceiver> receiverCaptor = 608 ArgumentCaptor<BroadcastReceiver> receiverCaptor =
343 ArgumentCaptor.forClass(BroadcastReceiver.class); 609 ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -375,13 +641,148 @@ public class CarUserManagerHelperTest {
375 assertThat(handlerCaptor.getValue()).isNull(); 641 assertThat(handlerCaptor.getValue()).isNull();
376 642
377 // Unregister the receiver. 643 // Unregister the receiver.
378 mHelper.unregisterOnUsersUpdateListener(); 644 mCarUserManagerHelper.unregisterOnUsersUpdateListener(mTestListener);
379 verify(mContext).unregisterReceiver(receiverCaptor.getValue()); 645 verify(mContext).unregisterReceiver(receiverCaptor.getValue());
380 } 646 }
381 647
648 @Test
649 public void testMultipleRegistrationsOfSameListener() {
650 CarUserManagerHelper.OnUsersUpdateListener listener =
651 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
652
653 ArgumentCaptor<BroadcastReceiver> receiverCaptor =
654 ArgumentCaptor.forClass(BroadcastReceiver.class);
655
656 mCarUserManagerHelper.registerOnUsersUpdateListener(listener);
657 mCarUserManagerHelper.registerOnUsersUpdateListener(listener);
658 // Even for multiple registrations of the same listener, broadcast receiver registered once.
659 verify(mContext, times(1))
660 .registerReceiverAsUser(receiverCaptor.capture(), any(), any(), any(), any());
661
662 // Verify that calling the receiver calls the listener.
663 receiverCaptor.getValue().onReceive(mContext, new Intent());
664 verify(listener).onUsersUpdate();
665
666 // Verify that a single removal unregisters the listener.
667 mCarUserManagerHelper.unregisterOnUsersUpdateListener(listener);
668 verify(mContext).unregisterReceiver(any());
669 }
670
671 @Test
672 public void testMultipleUnregistrationsOfTheSameListener() {
673 CarUserManagerHelper.OnUsersUpdateListener listener =
674 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
675 mCarUserManagerHelper.registerOnUsersUpdateListener(listener);
676
677 // Verify that a multiple unregistrations cause only one unregister for broadcast receiver.
678 mCarUserManagerHelper.unregisterOnUsersUpdateListener(listener);
679 mCarUserManagerHelper.unregisterOnUsersUpdateListener(listener);
680 mCarUserManagerHelper.unregisterOnUsersUpdateListener(listener);
681 verify(mContext, times(1)).unregisterReceiver(any());
682 }
683
684 @Test
685 public void testUnregisterReceiverCalledAfterAllListenersUnregister() {
686 CarUserManagerHelper.OnUsersUpdateListener listener1 =
687 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
688 CarUserManagerHelper.OnUsersUpdateListener listener2 =
689 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
690
691 mCarUserManagerHelper.registerOnUsersUpdateListener(listener1);
692 mCarUserManagerHelper.registerOnUsersUpdateListener(listener2);
693
694 mCarUserManagerHelper.unregisterOnUsersUpdateListener(listener1);
695 verify(mContext, never()).unregisterReceiver(any());
696
697 mCarUserManagerHelper.unregisterOnUsersUpdateListener(listener2);
698 verify(mContext, times(1)).unregisterReceiver(any());
699 }
700
701 @Test
702 public void testRegisteringMultipleListeners() {
703 CarUserManagerHelper.OnUsersUpdateListener listener1 =
704 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
705 CarUserManagerHelper.OnUsersUpdateListener listener2 =
706 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
707 ArgumentCaptor<BroadcastReceiver> receiverCaptor =
708 ArgumentCaptor.forClass(BroadcastReceiver.class);
709
710 mCarUserManagerHelper.registerOnUsersUpdateListener(listener1);
711 mCarUserManagerHelper.registerOnUsersUpdateListener(listener2);
712 verify(mContext, times(1))
713 .registerReceiverAsUser(receiverCaptor.capture(), any(), any(), any(), any());
714
715 // Verify that calling the receiver calls both listeners.
716 receiverCaptor.getValue().onReceive(mContext, new Intent());
717 verify(listener1).onUsersUpdate();
718 verify(listener2).onUsersUpdate();
719 }
720
721 @Test
722 public void testUnregisteringListenerStopsUpdatesForListener() {
723 CarUserManagerHelper.OnUsersUpdateListener listener1 =
724 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
725 CarUserManagerHelper.OnUsersUpdateListener listener2 =
726 Mockito.mock(CarUserManagerHelper.OnUsersUpdateListener.class);
727 ArgumentCaptor<BroadcastReceiver> receiverCaptor =
728 ArgumentCaptor.forClass(BroadcastReceiver.class);
729
730 mCarUserManagerHelper.registerOnUsersUpdateListener(listener1);
731 mCarUserManagerHelper.registerOnUsersUpdateListener(listener2);
732 verify(mContext, times(1))
733 .registerReceiverAsUser(receiverCaptor.capture(), any(), any(), any(), any());
734
735 // Unregister listener2
736 mCarUserManagerHelper.unregisterOnUsersUpdateListener(listener2);
737
738 // Verify that calling the receiver calls only one listener.
739 receiverCaptor.getValue().onReceive(mContext, new Intent());
740 verify(listener1).onUsersUpdate();
741 verify(listener2, never()).onUsersUpdate();
742 }
743
744 @Test
745 public void testGetInitialUserWithValidLastActiveUser() {
746 SystemProperties.set("android.car.systemuser.headless", "true");
747 int lastActiveUserId = 12;
748
749 UserInfo otherUser1 = createUserInfoForId(lastActiveUserId - 2);
750 UserInfo otherUser2 = createUserInfoForId(lastActiveUserId - 1);
751 UserInfo otherUser3 = createUserInfoForId(lastActiveUserId);
752
753 mCarUserManagerHelper.setLastActiveUser(
754 lastActiveUserId, /* skipGlobalSettings= */ true);
755 mockGetUsers(mSystemUser, otherUser1, otherUser2, otherUser3);
756
757 assertThat(mCarUserManagerHelper.getInitialUser()).isEqualTo(lastActiveUserId);
758 }
759
760 @Test
761 public void testGetInitialUserWithNonExistLastActiveUser() {
762 SystemProperties.set("android.car.systemuser.headless", "true");
763 int lastActiveUserId = 12;
764
765 UserInfo otherUser1 = createUserInfoForId(lastActiveUserId - 2);
766 UserInfo otherUser2 = createUserInfoForId(lastActiveUserId - 1);
767
768 mCarUserManagerHelper.setLastActiveUser(
769 lastActiveUserId, /* skipGlobalSettings= */ true);
770 mockGetUsers(mSystemUser, otherUser1, otherUser2);
771
772 assertThat(mCarUserManagerHelper.getInitialUser()).isEqualTo(lastActiveUserId - 2);
773 }
774
382 private UserInfo createUserInfoForId(int id) { 775 private UserInfo createUserInfoForId(int id) {
383 UserInfo userInfo = new UserInfo(); 776 UserInfo userInfo = new UserInfo();
384 userInfo.id = id; 777 userInfo.id = id;
385 return userInfo; 778 return userInfo;
386 } 779 }
780
781 private void mockGetUsers(UserInfo... users) {
782 List<UserInfo> testUsers = new ArrayList<>();
783 for (UserInfo user: users) {
784 testUsers.add(user);
785 }
786 doReturn(testUsers).when(mUserManager).getUsers(true);
787 }
387} 788}
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
index faa7bd09..48d447bc 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
@@ -21,18 +21,17 @@ import static com.google.common.truth.Truth.assertThat;
21import static org.mockito.ArgumentMatchers.eq; 21import static org.mockito.ArgumentMatchers.eq;
22import static org.mockito.Mockito.doReturn; 22import static org.mockito.Mockito.doReturn;
23import static org.mockito.Mockito.verify; 23import static org.mockito.Mockito.verify;
24import static org.mockito.Mockito.when;
25 24
26import android.car.user.CarUserManagerHelper; 25import android.car.user.CarUserManagerHelper;
27import android.content.Context; 26import android.content.Context;
28import android.content.Intent; 27import android.content.Intent;
29import android.content.IntentFilter; 28import android.content.IntentFilter;
30import android.content.pm.UserInfo; 29import android.content.pm.UserInfo;
30import android.location.LocationManager;
31import android.os.UserHandle;
32import android.os.UserManager;
31import android.support.test.runner.AndroidJUnit4; 33import android.support.test.runner.AndroidJUnit4;
32 34
33import java.util.ArrayList;
34import java.util.List;
35
36import org.junit.Before; 35import org.junit.Before;
37import org.junit.Test; 36import org.junit.Test;
38import org.junit.runner.RunWith; 37import org.junit.runner.RunWith;
@@ -40,6 +39,9 @@ import org.mockito.ArgumentCaptor;
40import org.mockito.Mock; 39import org.mockito.Mock;
41import org.mockito.MockitoAnnotations; 40import org.mockito.MockitoAnnotations;
42 41
42import java.util.ArrayList;
43import java.util.List;
44
43/** 45/**
44 * This class contains unit tests for the {@link CarUserService}. 46 * This class contains unit tests for the {@link CarUserService}.
45 * 47 *
@@ -60,17 +62,23 @@ public class CarUserServiceTest {
60 private Context mApplicationContext; 62 private Context mApplicationContext;
61 63
62 @Mock 64 @Mock
65 private LocationManager mLocationManager;
66
67 @Mock
63 private CarUserManagerHelper mCarUserManagerHelper; 68 private CarUserManagerHelper mCarUserManagerHelper;
64 69
65 /** 70 /**
66 * Initialize all of the objects with the @Mock annotation. 71 * Initialize all of the objects with the @Mock annotation.
67 */ 72 */
68 @Before 73 @Before
69 public void setUp() throws Exception { 74 public void setUpMocks() throws Exception {
70 MockitoAnnotations.initMocks(this); 75 MockitoAnnotations.initMocks(this);
71 when(mMockContext.getApplicationContext()).thenReturn(mApplicationContext); 76 doReturn(mApplicationContext).when(mMockContext).getApplicationContext();
77 doReturn(mLocationManager).when(mMockContext).getSystemService(Context.LOCATION_SERVICE);
72 78
73 mCarUserService = new CarUserService(mMockContext, mCarUserManagerHelper); 79 mCarUserService = new CarUserService(mMockContext, mCarUserManagerHelper);
80
81 doReturn(new ArrayList<>()).when(mCarUserManagerHelper).getAllUsers();
74 } 82 }
75 83
76 /** 84 /**
@@ -83,9 +91,10 @@ public class CarUserServiceTest {
83 mCarUserService.init(); 91 mCarUserService.init();
84 verify(mMockContext).registerReceiver(eq(mCarUserService), argument.capture()); 92 verify(mMockContext).registerReceiver(eq(mCarUserService), argument.capture());
85 IntentFilter intentFilter = argument.getValue(); 93 IntentFilter intentFilter = argument.getValue();
86 assertThat(intentFilter.countActions()).isEqualTo(1); 94 assertThat(intentFilter.countActions()).isEqualTo(2);
87 95
88 assertThat(intentFilter.getAction(0)).isEqualTo(Intent.ACTION_LOCKED_BOOT_COMPLETED); 96 assertThat(intentFilter.getAction(0)).isEqualTo(Intent.ACTION_LOCKED_BOOT_COMPLETED);
97 assertThat(intentFilter.getAction(1)).isEqualTo(Intent.ACTION_USER_SWITCHED);
89 } 98 }
90 99
91 /** 100 /**
@@ -102,20 +111,123 @@ public class CarUserServiceTest {
102 */ 111 */
103 @Test 112 @Test
104 public void testStartsSecondaryAdminUserOnFirstRun() { 113 public void testStartsSecondaryAdminUserOnFirstRun() {
114 UserInfo admin = mockAdmin(/* adminId= */ 10);
115
116 mCarUserService.onReceive(mMockContext,
117 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
118
119 verify(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
120 verify(mCarUserManagerHelper).switchToUser(admin);
121 }
122
123 /**
124 * Test that the {@link CarUserService} disable modify account for user 0 upon first run.
125 */
126 @Test
127 public void testDisableModifyAccountsForSystemUserOnFirstRun() {
128 // Mock system user.
129 UserInfo systemUser = new UserInfo();
130 systemUser.id = UserHandle.USER_SYSTEM;
131 doReturn(systemUser).when(mCarUserManagerHelper).getSystemUserInfo();
132
133 mockAdmin(10);
134
135 mCarUserService.onReceive(mMockContext,
136 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
137
138 verify(mCarUserManagerHelper)
139 .setUserRestriction(systemUser, UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
140 }
141
142 /**
143 * Test that the {@link CarUserService} disable location service for user 0 upon first run.
144 */
145 @Test
146 public void testDisableLocationForSystemUserOnFirstRun() {
147 mockAdmin(/* adminId= */ 10);
148
149 mCarUserService.onReceive(mMockContext,
150 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
151
152 verify(mLocationManager).setLocationEnabledForUser(
153 /* enabled= */ false, UserHandle.of(UserHandle.USER_SYSTEM));
154 }
155
156 /**
157 * Test that the {@link CarUserService} updates last active user to the first admin user
158 * on first run.
159 */
160 @Test
161 public void testUpdateLastActiveUserOnFirstRun() {
162 UserInfo admin = mockAdmin(/* adminId= */ 10);
163
164 mCarUserService.onReceive(mMockContext,
165 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
166
167 verify(mCarUserManagerHelper)
168 .setLastActiveUser(admin.id, /* skipGlobalSetting= */ false);
169 }
170
171 /**
172 * Test that the {@link CarUserService} starts up the last active user on reboot.
173 */
174 @Test
175 public void testStartsLastActiveUserOnReboot() {
105 List<UserInfo> users = new ArrayList<>(); 176 List<UserInfo> users = new ArrayList<>();
106 177
107 int adminUserId = 10; 178 int adminUserId = 10;
108 UserInfo admin = new UserInfo(adminUserId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN); 179 UserInfo admin = new UserInfo(adminUserId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
109 180
181 int secUserId = 11;
182 UserInfo secUser =
183 new UserInfo(secUserId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
184
185 users.add(admin);
186 users.add(secUser);
187
110 doReturn(users).when(mCarUserManagerHelper).getAllUsers(); 188 doReturn(users).when(mCarUserManagerHelper).getAllUsers();
111 // doReturn(users).when(mCarUserManagerHelper.getAllUsers()); 189 doReturn(secUserId).when(mCarUserManagerHelper).getInitialUser();
112 doReturn(admin).when(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
113 doReturn(true).when(mCarUserManagerHelper).switchToUser(admin);
114 190
115 mCarUserService.onReceive(mMockContext, 191 mCarUserService.onReceive(mMockContext,
116 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)); 192 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
117 193
118 verify(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME); 194 verify(mCarUserManagerHelper).switchToUserId(secUserId);
119 verify(mCarUserManagerHelper).switchToUser(admin); 195 }
196
197 /**
198 * Test that the {@link CarUserService} updates last active user on user switch intent.
199 */
200 @Test
201 public void testLastActiveUserUpdatedOnUserSwitch() {
202 int lastActiveUserId = 11;
203
204 Intent intent = new Intent(Intent.ACTION_USER_SWITCHED);
205 intent.putExtra(Intent.EXTRA_USER_HANDLE, lastActiveUserId);
206
207 doReturn(true).when(mCarUserManagerHelper).isPersistentUser(lastActiveUserId);
208
209 mCarUserService.onReceive(mMockContext, intent);
210
211 verify(mCarUserManagerHelper).setLastActiveUser(
212 lastActiveUserId, /* skipGlobalSetting= */ false);
213 }
214
215 /**
216 * Test that the {@link CarUserService} sets default guest restrictions on first boot.
217 */
218 @Test
219 public void testInitializeGuestRestrictionsOnFirstRun() {
220 mockAdmin(/* adminId= */ 10);
221
222 mCarUserService.onReceive(mMockContext,
223 new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
224
225 verify(mCarUserManagerHelper).initDefaultGuestRestrictions();
226 }
227
228 private UserInfo mockAdmin(int adminId) {
229 UserInfo admin = new UserInfo(adminId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
230 doReturn(admin).when(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
231 return admin;
120 } 232 }
121} 233}
diff --git a/tests/robotests/src/com/android/car/users/CarUserManagerHelperRoboTest.java b/tests/robotests/src/com/android/car/users/CarUserManagerHelperRoboTest.java
index 9c394459..fcfd6dc0 100644
--- a/tests/robotests/src/com/android/car/users/CarUserManagerHelperRoboTest.java
+++ b/tests/robotests/src/com/android/car/users/CarUserManagerHelperRoboTest.java
@@ -122,7 +122,7 @@ public class CarUserManagerHelperRoboTest {
122 } 122 }
123 123
124 @Test 124 @Test
125 public void testGetAllUsersExcludesForegroundUser() { 125 public void testGetAllUsersExceptForegroundUser() {
126 ShadowActivityManager.setCurrentUser(11); 126 ShadowActivityManager.setCurrentUser(11);
127 ShadowUserManager userManager = ShadowUserManager.getShadow(); 127 ShadowUserManager userManager = ShadowUserManager.getShadow();
128 128