diff options
author | Bill Yi | 2018-11-30 13:50:08 -0600 |
---|---|---|
committer | android-build-merger | 2018-11-30 13:50:08 -0600 |
commit | ed10dc727d239fa7857390a3787edc2c99e9339e (patch) | |
tree | d7b300e81f05e45345ada1ffcaf39b2512dd8f6f | |
parent | f02b56678700a4035d0ad8882f7d20371bb96ee2 (diff) | |
parent | 0f8320f6b95736ea4b703764a7d11a8a6ca13674 (diff) | |
download | platform-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
79 files changed, 4817 insertions, 951 deletions
@@ -5,3 +5,4 @@ | |||
5 | gen/ | 5 | gen/ |
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 | ||
17 | package com.android.car.trust; | 17 | package com.android.car.trust; |
18 | 18 | ||
19 | import android.bluetooth.BluetoothDevice; | 19 | import android.annotation.Nullable; |
20 | import android.bluetooth.BluetoothGattServer; | 20 | import android.app.ActivityManager; |
21 | import android.bluetooth.BluetoothGattServerCallback; | 21 | import android.app.UserSwitchObserver; |
22 | import android.bluetooth.BluetoothManager; | 22 | import android.bluetooth.BluetoothAdapter; |
23 | import android.car.trust.ICarTrustAgentBleService; | 23 | import android.car.trust.ICarTrustAgentBleService; |
24 | import android.car.trust.ICarTrustAgentTokenRequestDelegate; | 24 | import android.car.trust.ICarTrustAgentTokenRequestDelegate; |
25 | import android.car.trust.ICarTrustAgentUnlockCallback; | 25 | import android.car.trust.ICarTrustAgentUnlockCallback; |
26 | import android.content.BroadcastReceiver; | ||
26 | import android.content.ComponentName; | 27 | import android.content.ComponentName; |
27 | import android.content.Context; | 28 | import android.content.Context; |
28 | import android.content.Intent; | 29 | import android.content.Intent; |
30 | import android.content.IntentFilter; | ||
29 | import android.content.ServiceConnection; | 31 | import android.content.ServiceConnection; |
30 | import android.os.Handler; | ||
31 | import android.os.IBinder; | 32 | import android.os.IBinder; |
32 | import android.os.RemoteException; | 33 | import android.os.RemoteException; |
33 | import android.os.UserHandle; | 34 | import android.os.UserHandle; |
34 | import android.os.UserManager; | ||
35 | import android.service.trust.TrustAgentService; | 35 | import android.service.trust.TrustAgentService; |
36 | import android.util.Log; | 36 | import android.util.Log; |
37 | 37 | ||
38 | import 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; | |||
25 | import android.car.trust.ICarTrustAgentTokenResponseCallback; | 25 | import android.car.trust.ICarTrustAgentTokenResponseCallback; |
26 | import android.car.trust.ICarTrustAgentUnlockCallback; | 26 | import android.car.trust.ICarTrustAgentUnlockCallback; |
27 | import android.content.Intent; | 27 | import android.content.Intent; |
28 | import android.content.SharedPreferences; | ||
28 | import android.os.IBinder; | 29 | import android.os.IBinder; |
29 | import android.os.ParcelUuid; | 30 | import android.os.ParcelUuid; |
30 | import android.os.RemoteCallbackList; | 31 | import 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 | ||
78 | package android.car.app.menu { | 198 | package 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 | ||
117 | package android.car.cluster { | 123 | package 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 | ||
17 | package android.car; | 17 | package android.car; |
18 | 18 | ||
19 | import static java.lang.Integer.toHexString; | ||
20 | |||
21 | import android.annotation.Nullable; | 19 | import android.annotation.Nullable; |
22 | import android.car.annotation.ValueTypeDef; | 20 | import android.car.annotation.ValueTypeDef; |
23 | import android.car.hardware.CarPropertyValue; | 21 | import android.car.hardware.CarPropertyValue; |
24 | import android.car.hardware.property.ICarProperty; | 22 | import android.car.hardware.property.CarPropertyManager; |
25 | import android.os.Bundle; | 23 | import android.os.Bundle; |
26 | import android.os.IBinder; | 24 | import android.os.IBinder; |
27 | import android.os.RemoteException; | ||
28 | import 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 | */ | ||
16 | package 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 | */ | ||
25 | public 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 | */ | ||
16 | package 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 | */ | ||
24 | public 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 | */ | ||
16 | package 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 | */ | ||
24 | public 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 | */ | ||
16 | package android.car; | ||
17 | |||
18 | import android.annotation.SystemApi; | ||
19 | |||
20 | /** | ||
21 | * Define value for getAccess() in {@link android.car.hardware.CarPropertyConfig} | ||
22 | * @hide | ||
23 | */ | ||
24 | @SystemApi | ||
25 | public 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 | |||
17 | package 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 | */ | ||
25 | public 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; | |||
24 | import java.lang.annotation.RetentionPolicy; | 24 | import 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 | ||
19 | import static java.lang.Integer.toHexString; | 19 | import static java.lang.Integer.toHexString; |
20 | 20 | ||
21 | import android.annotation.Nullable; | ||
21 | import android.car.CarApiUtil; | 22 | import android.car.CarApiUtil; |
22 | import android.car.CarManagerBase; | 23 | import android.car.CarManagerBase; |
23 | import android.car.CarNotConnectedException; | 24 | import android.car.CarNotConnectedException; |
@@ -44,6 +45,7 @@ import java.util.function.Consumer; | |||
44 | * @hide | 45 | * @hide |
45 | */ | 46 | */ |
46 | public class CarPropertyManager implements CarManagerBase { | 47 | public 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 | */ |
16 | package android.car.user; | 16 | package android.car.user; |
17 | 17 | ||
18 | import android.Manifest; | ||
18 | import android.annotation.Nullable; | 19 | import android.annotation.Nullable; |
20 | import android.annotation.RequiresPermission; | ||
19 | import android.app.ActivityManager; | 21 | import android.app.ActivityManager; |
20 | import android.car.settings.CarSettings; | 22 | import android.car.settings.CarSettings; |
21 | import android.content.BroadcastReceiver; | 23 | import android.content.BroadcastReceiver; |
@@ -26,15 +28,21 @@ import android.content.pm.UserInfo; | |||
26 | import android.graphics.Bitmap; | 28 | import android.graphics.Bitmap; |
27 | import android.graphics.drawable.BitmapDrawable; | 29 | import android.graphics.drawable.BitmapDrawable; |
28 | import android.graphics.drawable.Drawable; | 30 | import android.graphics.drawable.Drawable; |
31 | import android.os.Bundle; | ||
29 | import android.os.SystemProperties; | 32 | import android.os.SystemProperties; |
30 | import android.os.UserHandle; | 33 | import android.os.UserHandle; |
31 | import android.os.UserManager; | 34 | import android.os.UserManager; |
35 | import android.provider.Settings; | ||
32 | import android.util.Log; | 36 | import android.util.Log; |
33 | 37 | ||
34 | import com.android.internal.util.UserIcons; | 38 | import com.android.internal.util.UserIcons; |
35 | 39 | ||
40 | import com.google.android.collect.Sets; | ||
41 | |||
42 | import java.util.ArrayList; | ||
36 | import java.util.Iterator; | 43 | import java.util.Iterator; |
37 | import java.util.List; | 44 | import java.util.List; |
45 | import 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; | |||
49 | public class CarUserManagerHelper { | 57 | public 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 | ||
17 | const static char kEnumeratorServiceName[] = "EvsEnumeratorHw"; | 19 | const 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 | ||
28 | LOCAL_SRC_FILES := $(car_service_sources) | 28 | LOCAL_SRC_FILES := $(car_service_sources) |
29 | 29 | ||
30 | LOCAL_USE_AAPT2 := true | ||
31 | |||
30 | LOCAL_PACKAGE_NAME := CarService | 32 | LOCAL_PACKAGE_NAME := CarService |
31 | LOCAL_PRIVATE_PLATFORM_APIS := true | 33 | LOCAL_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 | ||
52 | include frameworks/base/packages/SettingsLib/common.mk | ||
53 | |||
50 | include $(BUILD_PACKAGE) | 54 | include $(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 | ||
79 | include frameworks/base/packages/SettingsLib/common.mk | ||
80 | |||
75 | include $(BUILD_STATIC_JAVA_LIBRARY) | 81 | include $(BUILD_STATIC_JAVA_LIBRARY) |
76 | 82 | ||
77 | include $(call all-makefiles-under,$(LOCAL_PATH)) | 83 | include $(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; | |||
18 | import android.annotation.NonNull; | 18 | import android.annotation.NonNull; |
19 | import android.annotation.Nullable; | 19 | import android.annotation.Nullable; |
20 | import android.car.Car; | 20 | import android.car.Car; |
21 | import android.car.media.CarAudioManager; | ||
21 | import android.car.media.CarAudioPatchHandle; | 22 | import android.car.media.CarAudioPatchHandle; |
22 | import android.car.media.ICarAudio; | 23 | import android.car.media.ICarAudio; |
23 | import android.car.media.ICarVolumeCallback; | 24 | import android.car.media.ICarVolumeCallback; |
@@ -45,11 +46,13 @@ import android.media.audiopolicy.AudioPolicy; | |||
45 | import android.os.IBinder; | 46 | import android.os.IBinder; |
46 | import android.os.Looper; | 47 | import android.os.Looper; |
47 | import android.os.RemoteException; | 48 | import android.os.RemoteException; |
49 | import android.provider.Settings; | ||
48 | import android.telephony.TelephonyManager; | 50 | import android.telephony.TelephonyManager; |
49 | import android.text.TextUtils; | 51 | import android.text.TextUtils; |
50 | import android.util.Log; | 52 | import android.util.Log; |
51 | import android.util.SparseArray; | 53 | import android.util.SparseArray; |
52 | import android.util.SparseIntArray; | 54 | import android.util.SparseIntArray; |
55 | import android.view.KeyEvent; | ||
53 | 56 | ||
54 | import com.android.internal.util.Preconditions; | 57 | import 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 @@ | |||
16 | package com.android.car; | 16 | package com.android.car; |
17 | 17 | ||
18 | import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; | 18 | import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; |
19 | import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE; | ||
19 | 20 | ||
21 | import android.app.ActivityManager; | ||
20 | import android.car.input.CarInputHandlingService; | 22 | import android.car.input.CarInputHandlingService; |
21 | import android.car.input.CarInputHandlingService.InputFilter; | 23 | import android.car.input.CarInputHandlingService.InputFilter; |
22 | import android.car.input.ICarInputListener; | 24 | import android.car.input.ICarInputListener; |
@@ -30,19 +32,18 @@ import android.os.Binder; | |||
30 | import android.os.Bundle; | 32 | import android.os.Bundle; |
31 | import android.os.IBinder; | 33 | import android.os.IBinder; |
32 | import android.os.Parcel; | 34 | import android.os.Parcel; |
33 | import android.os.ParcelFileDescriptor; | ||
34 | import android.os.RemoteException; | 35 | import android.os.RemoteException; |
35 | import android.os.SystemClock; | 36 | import android.os.SystemClock; |
36 | import android.os.UserHandle; | 37 | import android.os.UserHandle; |
37 | import android.provider.CallLog.Calls; | 38 | import android.provider.CallLog.Calls; |
38 | import android.speech.RecognizerIntent; | ||
39 | import android.telecom.TelecomManager; | 39 | import android.telecom.TelecomManager; |
40 | import android.text.TextUtils; | 40 | import android.text.TextUtils; |
41 | import android.util.Log; | 41 | import android.util.Log; |
42 | import android.view.KeyEvent; | 42 | import android.view.KeyEvent; |
43 | 43 | ||
44 | import com.android.car.hal.InputHalService; | 44 | import com.android.car.hal.InputHalService; |
45 | import com.android.car.hal.VehicleHal; | 45 | import com.android.internal.app.AssistUtils; |
46 | import com.android.internal.app.IVoiceInteractionSessionShowCallback; | ||
46 | 47 | ||
47 | import java.io.PrintWriter; | 48 | import java.io.PrintWriter; |
48 | import java.util.HashMap; | 49 | import 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; | |||
19 | import android.car.hardware.CarPropertyValue; | 19 | import android.car.hardware.CarPropertyValue; |
20 | import android.car.hardware.property.CarPropertyEvent; | 20 | import android.car.hardware.property.CarPropertyEvent; |
21 | import android.car.hardware.property.ICarPropertyEventListener; | 21 | import android.car.hardware.property.ICarPropertyEventListener; |
22 | import android.car.user.CarUserManagerHelper; | ||
22 | import android.content.BroadcastReceiver; | 23 | import android.content.BroadcastReceiver; |
23 | import android.content.Context; | 24 | import android.content.Context; |
24 | import android.content.Intent; | 25 | import android.content.Intent; |
@@ -31,6 +32,7 @@ import android.os.Handler; | |||
31 | import android.os.HandlerThread; | 32 | import android.os.HandlerThread; |
32 | import android.os.RemoteException; | 33 | import android.os.RemoteException; |
33 | import android.os.SystemClock; | 34 | import android.os.SystemClock; |
35 | import android.os.UserHandle; | ||
34 | import android.util.AtomicFile; | 36 | import android.util.AtomicFile; |
35 | import android.util.JsonReader; | 37 | import android.util.JsonReader; |
36 | import android.util.JsonWriter; | 38 | import 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; | |||
27 | import android.car.hardware.property.CarPropertyEvent; | 27 | import android.car.hardware.property.CarPropertyEvent; |
28 | import android.car.hardware.property.ICarPropertyEventListener; | 28 | import android.car.hardware.property.ICarPropertyEventListener; |
29 | import android.content.Context; | 29 | import android.content.Context; |
30 | import android.content.pm.PackageManager; | ||
30 | import android.hardware.automotive.vehicle.V2_0.VehicleProperty; | 31 | import android.hardware.automotive.vehicle.V2_0.VehicleProperty; |
32 | import android.os.Binder; | ||
33 | import android.os.Build; | ||
31 | import android.os.IBinder; | 34 | import android.os.IBinder; |
35 | import android.os.Process; | ||
32 | import android.os.RemoteException; | 36 | import android.os.RemoteException; |
33 | import android.util.Log; | 37 | import android.util.Log; |
34 | 38 | ||
39 | import com.android.internal.annotations.GuardedBy; | ||
40 | |||
35 | import org.xmlpull.v1.XmlPullParserException; | 41 | import org.xmlpull.v1.XmlPullParserException; |
36 | 42 | ||
37 | import java.io.IOException; | 43 | import 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; | |||
36 | import android.util.TimingsTraceLog; | 36 | import android.util.TimingsTraceLog; |
37 | 37 | ||
38 | import com.android.car.cluster.InstrumentClusterService; | 38 | import com.android.car.cluster.InstrumentClusterService; |
39 | import com.android.car.garagemode.GarageModeService; | ||
39 | import com.android.car.hal.VehicleHal; | 40 | import com.android.car.hal.VehicleHal; |
40 | import com.android.car.internal.FeatureConfiguration; | 41 | import com.android.car.internal.FeatureConfiguration; |
41 | import com.android.car.pm.CarPackageManagerService; | 42 | import 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 | |||
17 | package com.android.car.garagemode; | ||
18 | |||
19 | import android.content.Context; | ||
20 | import android.util.Log; | ||
21 | |||
22 | import com.android.car.R; | ||
23 | |||
24 | import java.util.HashMap; | ||
25 | import java.util.LinkedList; | ||
26 | import java.util.List; | ||
27 | import 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 | */ | ||
35 | public 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 | */ |
16 | package com.android.car; | ||
17 | 16 | ||
18 | import android.car.CarApiUtil; | 17 | package com.android.car.garagemode; |
18 | |||
19 | import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_ENABLED_URI; | ||
20 | import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_MAINTENANCE_WINDOW_URI; | ||
21 | import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_WAKE_UP_TIME_URI; | ||
22 | |||
19 | import android.car.settings.CarSettings; | 23 | import android.car.settings.CarSettings; |
20 | import android.car.settings.GarageModeSettingsObserver; | 24 | import android.car.settings.GarageModeSettingsObserver; |
21 | import android.content.Context; | 25 | import android.content.Context; |
@@ -32,32 +36,28 @@ import android.preference.PreferenceManager; | |||
32 | import android.provider.Settings; | 36 | import android.provider.Settings; |
33 | import android.util.Log; | 37 | import android.util.Log; |
34 | 38 | ||
39 | import com.android.car.CarPowerManagementService; | ||
40 | import com.android.car.CarServiceBase; | ||
41 | import com.android.car.DeviceIdleControllerWrapper; | ||
35 | import com.android.internal.annotations.GuardedBy; | 42 | import com.android.internal.annotations.GuardedBy; |
36 | import com.android.internal.annotations.VisibleForTesting; | 43 | import com.android.internal.annotations.VisibleForTesting; |
37 | 44 | ||
38 | import java.io.PrintWriter; | 45 | import java.io.PrintWriter; |
39 | import java.lang.RuntimeException; | ||
40 | import java.util.Map; | ||
41 | import java.util.HashMap; | ||
42 | |||
43 | import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_ENABLED_URI; | ||
44 | import static android.car.settings.GarageModeSettingsObserver.GARAGE_MODE_MAINTENANCE_WINDOW_URI; | ||
45 | import 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 | */ |
55 | public class GarageModeService implements CarServiceBase, | 56 | public 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 | |||
17 | package 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 | */ | ||
24 | class 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; | |||
40 | import android.content.pm.Signature; | 40 | import android.content.pm.Signature; |
41 | import android.content.res.Resources; | 41 | import android.content.res.Resources; |
42 | import android.os.Binder; | 42 | import android.os.Binder; |
43 | import android.os.Build; | ||
43 | import android.os.Handler; | 44 | import android.os.Handler; |
44 | import android.os.HandlerThread; | 45 | import android.os.HandlerThread; |
45 | import android.os.Looper; | 46 | import android.os.Looper; |
46 | import android.os.Message; | 47 | import android.os.Message; |
47 | import android.os.Process; | 48 | import android.os.Process; |
49 | import android.text.TextUtils; | ||
48 | import android.text.format.DateFormat; | 50 | import android.text.format.DateFormat; |
49 | import android.util.ArraySet; | 51 | import android.util.ArraySet; |
50 | import android.util.Log; | 52 | import 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 | ||
17 | package com.android.car.systeminterface; | 17 | package com.android.car.systeminterface; |
18 | 18 | ||
19 | import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX; | ||
20 | import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear; | ||
21 | import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma; | ||
22 | |||
19 | import android.content.ContentResolver; | 23 | import android.content.ContentResolver; |
20 | import android.content.Context; | 24 | import android.content.Context; |
21 | import android.database.ContentObserver; | 25 | import 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 @@ | |||
17 | package com.android.car.user; | 17 | package com.android.car.user; |
18 | 18 | ||
19 | import android.annotation.Nullable; | 19 | import android.annotation.Nullable; |
20 | import android.car.settings.CarSettings; | ||
21 | import android.car.user.CarUserManagerHelper; | 20 | import android.car.user.CarUserManagerHelper; |
22 | import android.content.BroadcastReceiver; | 21 | import android.content.BroadcastReceiver; |
23 | import android.content.Context; | 22 | import android.content.Context; |
24 | import android.content.Intent; | 23 | import android.content.Intent; |
25 | import android.content.IntentFilter; | 24 | import android.content.IntentFilter; |
26 | import android.content.pm.UserInfo; | 25 | import android.content.pm.UserInfo; |
26 | import android.location.LocationManager; | ||
27 | import android.os.UserHandle; | ||
28 | import android.os.UserManager; | ||
27 | import android.util.Log; | 29 | import android.util.Log; |
28 | 30 | ||
29 | import com.android.car.CarServiceBase; | 31 | import 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 | */ |
42 | public class CarUserService extends BroadcastReceiver implements CarServiceBase { | 44 | public 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 @@ | |||
1 | LOCAL_PATH:= $(call my-dir) | ||
2 | include $(CLEAR_VARS) | ||
3 | |||
4 | LOCAL_PACKAGE_NAME := CarTrustAgentClient | ||
5 | |||
6 | LOCAL_USE_AAPT2 := true | ||
7 | LOCAL_SRC_FILES := $(call all-java-files-under, src) | ||
8 | LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4 | ||
9 | |||
10 | LOCAL_CERTIFICATE := platform | ||
11 | LOCAL_MODULE_TAGS := optional | ||
12 | LOCAL_MIN_SDK_VERSION := 23 | ||
13 | LOCAL_SDK_VERSION := current | ||
14 | |||
15 | include $(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 @@ | |||
1 | IMPORTANT NOTE: This is a reference app to smart unlock paired HU during development. | ||
2 | Consider 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 | */ | ||
16 | package com.android.car.trust.client; | ||
17 | |||
18 | import android.Manifest; | ||
19 | import android.app.Activity; | ||
20 | import android.content.pm.PackageManager; | ||
21 | import 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 | */ | ||
32 | public 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 | */ | ||
16 | package com.android.car.trust.client; | ||
17 | |||
18 | import android.bluetooth.BluetoothDevice; | ||
19 | import android.bluetooth.BluetoothGatt; | ||
20 | import android.bluetooth.BluetoothGattCharacteristic; | ||
21 | import android.bluetooth.BluetoothGattService; | ||
22 | import android.content.Context; | ||
23 | import android.content.SharedPreferences; | ||
24 | import android.os.Handler; | ||
25 | import android.os.ParcelUuid; | ||
26 | import android.preference.PreferenceManager; | ||
27 | import android.util.Base64; | ||
28 | import android.util.Log; | ||
29 | import android.widget.Button; | ||
30 | import android.widget.TextView; | ||
31 | |||
32 | import java.nio.ByteBuffer; | ||
33 | import java.util.Random; | ||
34 | import 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 | */ | ||
40 | public 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 | */ | ||
16 | package com.android.car.trust.client; | ||
17 | |||
18 | import android.bluetooth.BluetoothDevice; | ||
19 | import android.bluetooth.BluetoothGatt; | ||
20 | import android.bluetooth.BluetoothGattCharacteristic; | ||
21 | import android.bluetooth.BluetoothGattService; | ||
22 | import android.content.Context; | ||
23 | import android.content.SharedPreferences; | ||
24 | import android.os.Handler; | ||
25 | import android.os.ParcelUuid; | ||
26 | import android.preference.PreferenceManager; | ||
27 | import android.util.Base64; | ||
28 | import android.util.Log; | ||
29 | import android.widget.Button; | ||
30 | import android.widget.TextView; | ||
31 | |||
32 | import java.util.UUID; | ||
33 | |||
34 | /** | ||
35 | * A controller that sets up a {@link SimpleBleClient} to connect to the BLE unlock service. | ||
36 | */ | ||
37 | public 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 | */ | ||
16 | package com.android.car.trust.client; | ||
17 | |||
18 | import android.bluetooth.BluetoothDevice; | ||
19 | import android.bluetooth.BluetoothGatt; | ||
20 | import android.bluetooth.BluetoothGattCallback; | ||
21 | import android.bluetooth.BluetoothGattCharacteristic; | ||
22 | import android.bluetooth.BluetoothGattService; | ||
23 | import android.bluetooth.BluetoothManager; | ||
24 | import android.bluetooth.BluetoothProfile; | ||
25 | import android.bluetooth.le.BluetoothLeScanner; | ||
26 | import android.bluetooth.le.ScanCallback; | ||
27 | import android.bluetooth.le.ScanFilter; | ||
28 | import android.bluetooth.le.ScanResult; | ||
29 | import android.bluetooth.le.ScanSettings; | ||
30 | import android.content.Context; | ||
31 | import android.os.Handler; | ||
32 | import android.os.ParcelUuid; | ||
33 | import android.util.Log; | ||
34 | |||
35 | import androidx.annotation.NonNull; | ||
36 | |||
37 | import java.util.ArrayList; | ||
38 | import java.util.List; | ||
39 | import java.util.Queue; | ||
40 | import 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 | */ | ||
46 | public 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 | */ | ||
16 | package com.android.car.trust.client; | ||
17 | |||
18 | import android.bluetooth.BluetoothGattCharacteristic; | ||
19 | import android.bluetooth.BluetoothGattService; | ||
20 | import android.content.Context; | ||
21 | |||
22 | import java.nio.ByteBuffer; | ||
23 | import java.util.UUID; | ||
24 | |||
25 | public 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; | |||
23 | import android.car.hardware.property.CarPropertyManager; | 23 | import android.car.hardware.property.CarPropertyManager; |
24 | import android.content.Intent; | 24 | import android.content.Intent; |
25 | import android.content.pm.PackageManager; | 25 | import android.content.pm.PackageManager; |
26 | import android.os.AsyncTask; | ||
26 | import android.os.Bundle; | 27 | import android.os.Bundle; |
28 | import android.os.Handler; | ||
27 | import android.support.car.Car; | 29 | import android.support.car.Car; |
28 | import android.support.car.CarAppFocusManager; | 30 | import android.support.car.CarAppFocusManager; |
29 | import android.support.car.CarConnectionCallback; | 31 | import android.support.car.CarConnectionCallback; |
@@ -42,6 +44,7 @@ import com.google.android.car.kitchensink.audio.AudioTestFragment; | |||
42 | import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment; | 44 | import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment; |
43 | import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment; | 45 | import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment; |
44 | import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment; | 46 | import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment; |
47 | import com.google.android.car.kitchensink.connectivity.ConnectivityFragment; | ||
45 | import com.google.android.car.kitchensink.cube.CubesTestFragment; | 48 | import com.google.android.car.kitchensink.cube.CubesTestFragment; |
46 | import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment; | 49 | import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment; |
47 | import com.google.android.car.kitchensink.displayinfo.DisplayInfoFragment; | 50 | import 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 | |||
17 | package com.google.android.car.kitchensink.connectivity; | ||
18 | |||
19 | import android.annotation.Nullable; | ||
20 | import android.annotation.SuppressLint; | ||
21 | import android.net.ConnectivityManager; | ||
22 | import android.net.ConnectivityManager.NetworkCallback; | ||
23 | import android.net.Network; | ||
24 | import android.net.NetworkCapabilities; | ||
25 | import android.net.NetworkInfo; | ||
26 | import android.net.NetworkRequest; | ||
27 | import android.os.Bundle; | ||
28 | import android.os.Handler; | ||
29 | import android.support.v4.app.Fragment; | ||
30 | import android.util.Log; | ||
31 | import android.view.LayoutInflater; | ||
32 | import android.view.View; | ||
33 | import android.view.ViewGroup; | ||
34 | import android.widget.ArrayAdapter; | ||
35 | import android.widget.ListView; | ||
36 | import android.widget.Toast; | ||
37 | |||
38 | import com.google.android.car.kitchensink.R; | ||
39 | |||
40 | import java.util.ArrayList; | ||
41 | |||
42 | @SuppressLint("SetTextI18n") | ||
43 | public 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; | |||
25 | import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat; | 25 | import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat; |
26 | import android.hardware.automotive.vehicle.V2_0.VehicleAreaWindow; | 26 | import android.hardware.automotive.vehicle.V2_0.VehicleAreaWindow; |
27 | import android.os.Bundle; | 27 | import android.os.Bundle; |
28 | import android.os.Handler; | ||
28 | import android.support.v4.app.Fragment; | 29 | import android.support.v4.app.Fragment; |
29 | import android.util.Log; | 30 | import android.util.Log; |
30 | import android.view.LayoutInflater; | 31 | import 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; | |||
20 | import android.car.hardware.power.CarPowerManager; | 20 | import android.car.hardware.power.CarPowerManager; |
21 | import android.content.Context; | 21 | import android.content.Context; |
22 | import android.os.Bundle; | 22 | import android.os.Bundle; |
23 | import android.os.Handler; | ||
23 | import android.os.PowerManager; | 24 | import android.os.PowerManager; |
24 | import android.os.SystemClock; | 25 | import android.os.SystemClock; |
25 | import android.support.v4.app.Fragment; | 26 | import 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; | |||
26 | import android.hardware.automotive.vehicle.V2_0.VehicleProperty; | 26 | import android.hardware.automotive.vehicle.V2_0.VehicleProperty; |
27 | import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType; | 27 | import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType; |
28 | import android.os.Bundle; | 28 | import android.os.Bundle; |
29 | import android.os.Handler; | ||
29 | import android.support.v4.app.Fragment; | 30 | import android.support.v4.app.Fragment; |
30 | import android.util.Log; | 31 | import android.util.Log; |
31 | import android.view.LayoutInflater; | 32 | import 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 | */ |
16 | package com.android.car; | ||
17 | 16 | ||
18 | import static org.junit.Assert.assertArrayEquals; | 17 | package com.android.car.garagemode; |
18 | |||
19 | import static org.junit.Assert.assertEquals; | 19 | import static org.junit.Assert.assertEquals; |
20 | import static org.junit.Assert.assertNotNull; | ||
21 | import static org.junit.Assert.assertNull; | ||
22 | import static org.junit.Assert.assertTrue; | 20 | import static org.junit.Assert.assertTrue; |
23 | 21 | ||
24 | import android.car.settings.CarSettings; | 22 | import android.car.settings.CarSettings; |
@@ -31,16 +29,19 @@ import android.support.test.annotation.UiThreadTest; | |||
31 | import android.support.test.filters.MediumTest; | 29 | import android.support.test.filters.MediumTest; |
32 | import android.support.test.runner.AndroidJUnit4; | 30 | import android.support.test.runner.AndroidJUnit4; |
33 | 31 | ||
34 | import com.android.car.GarageModeService.GarageModePolicy; | 32 | import com.android.car.CarPowerManagementService; |
35 | import com.android.car.GarageModeService.WakeupTime; | 33 | import com.android.car.DeviceIdleControllerWrapper; |
34 | import com.android.car.R; | ||
36 | 35 | ||
37 | import org.junit.Test; | 36 | import org.junit.Test; |
38 | import org.junit.runner.RunWith; | 37 | import org.junit.runner.RunWith; |
39 | 38 | ||
39 | import java.util.List; | ||
40 | |||
40 | @RunWith(AndroidJUnit4.class) | 41 | @RunWith(AndroidJUnit4.class) |
41 | @MediumTest | 42 | @MediumTest |
42 | public class GarageModeTest { | 43 | public 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; | |||
31 | import android.car.hardware.CarSensorManager; | 31 | import android.car.hardware.CarSensorManager; |
32 | import android.car.hardware.property.CarPropertyEvent; | 32 | import android.car.hardware.property.CarPropertyEvent; |
33 | import android.car.hardware.property.ICarPropertyEventListener; | 33 | import android.car.hardware.property.ICarPropertyEventListener; |
34 | import android.car.user.CarUserManagerHelper; | ||
34 | import android.content.Context; | 35 | import android.content.Context; |
35 | import android.content.Intent; | 36 | import android.content.Intent; |
36 | import android.content.IntentFilter; | 37 | import 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) |
78 | public class CarLocationServiceTest { | 81 | public 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 | ||
19 | import static com.google.common.truth.Truth.assertThat; | 19 | import static com.google.common.truth.Truth.assertThat; |
20 | 20 | ||
21 | import static org.mockito.ArgumentMatchers.anyInt; | ||
22 | import static org.mockito.Matchers.any; | ||
23 | import static org.mockito.Mockito.doReturn; | ||
24 | import static org.mockito.Mockito.never; | ||
25 | import static org.mockito.Mockito.times; | ||
21 | import static org.mockito.Mockito.verify; | 26 | import static org.mockito.Mockito.verify; |
22 | import static org.mockito.Mockito.when; | ||
23 | 27 | ||
24 | import android.app.ActivityManager; | 28 | import android.app.ActivityManager; |
25 | import android.car.user.CarUserManagerHelper; | 29 | import android.car.user.CarUserManagerHelper; |
@@ -30,11 +34,13 @@ import android.content.IntentFilter; | |||
30 | import android.content.pm.UserInfo; | 34 | import android.content.pm.UserInfo; |
31 | import android.graphics.Bitmap; | 35 | import android.graphics.Bitmap; |
32 | import android.graphics.drawable.Drawable; | 36 | import android.graphics.drawable.Drawable; |
37 | import android.os.Bundle; | ||
33 | import android.os.Handler; | 38 | import android.os.Handler; |
34 | import android.os.SystemProperties; | 39 | import android.os.SystemProperties; |
35 | import android.os.UserHandle; | 40 | import android.os.UserHandle; |
36 | import android.os.UserManager; | 41 | import android.os.UserManager; |
37 | import android.support.test.InstrumentationRegistry; | 42 | import android.support.test.InstrumentationRegistry; |
43 | import android.support.test.filters.SmallTest; | ||
38 | import android.support.test.runner.AndroidJUnit4; | 44 | import android.support.test.runner.AndroidJUnit4; |
39 | 45 | ||
40 | import org.junit.Before; | 46 | import org.junit.Before; |
@@ -42,6 +48,7 @@ import org.junit.Test; | |||
42 | import org.junit.runner.RunWith; | 48 | import org.junit.runner.RunWith; |
43 | import org.mockito.ArgumentCaptor; | 49 | import org.mockito.ArgumentCaptor; |
44 | import org.mockito.Mock; | 50 | import org.mockito.Mock; |
51 | import org.mockito.Mockito; | ||
45 | import org.mockito.MockitoAnnotations; | 52 | import org.mockito.MockitoAnnotations; |
46 | 53 | ||
47 | import java.util.ArrayList; | 54 | import 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 | ||
61 | public class CarUserManagerHelperTest { | 69 | public 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; | |||
21 | import static org.mockito.ArgumentMatchers.eq; | 21 | import static org.mockito.ArgumentMatchers.eq; |
22 | import static org.mockito.Mockito.doReturn; | 22 | import static org.mockito.Mockito.doReturn; |
23 | import static org.mockito.Mockito.verify; | 23 | import static org.mockito.Mockito.verify; |
24 | import static org.mockito.Mockito.when; | ||
25 | 24 | ||
26 | import android.car.user.CarUserManagerHelper; | 25 | import android.car.user.CarUserManagerHelper; |
27 | import android.content.Context; | 26 | import android.content.Context; |
28 | import android.content.Intent; | 27 | import android.content.Intent; |
29 | import android.content.IntentFilter; | 28 | import android.content.IntentFilter; |
30 | import android.content.pm.UserInfo; | 29 | import android.content.pm.UserInfo; |
30 | import android.location.LocationManager; | ||
31 | import android.os.UserHandle; | ||
32 | import android.os.UserManager; | ||
31 | import android.support.test.runner.AndroidJUnit4; | 33 | import android.support.test.runner.AndroidJUnit4; |
32 | 34 | ||
33 | import java.util.ArrayList; | ||
34 | import java.util.List; | ||
35 | |||
36 | import org.junit.Before; | 35 | import org.junit.Before; |
37 | import org.junit.Test; | 36 | import org.junit.Test; |
38 | import org.junit.runner.RunWith; | 37 | import org.junit.runner.RunWith; |
@@ -40,6 +39,9 @@ import org.mockito.ArgumentCaptor; | |||
40 | import org.mockito.Mock; | 39 | import org.mockito.Mock; |
41 | import org.mockito.MockitoAnnotations; | 40 | import org.mockito.MockitoAnnotations; |
42 | 41 | ||
42 | import java.util.ArrayList; | ||
43 | import 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 | ||