Merge "Send global type property event in register" into pi-dev
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
index ff8c4db..373c23a 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyManager.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -178,8 +178,12 @@
*/
public void unregisterListener(CarPropertyEventListener listener) {
synchronized (mActivePropertyListener) {
+ int [] propertyIds = new int[mActivePropertyListener.size()];
for (int i = 0; i < mActivePropertyListener.size(); i++) {
- doUnregisterListenerLocked(listener, mActivePropertyListener.keyAt(i));
+ propertyIds[i] = mActivePropertyListener.keyAt(i);
+ }
+ for (int prop : propertyIds) {
+ doUnregisterListenerLocked(listener, prop);
}
}
}
diff --git a/car-lib/src/android/car/user/CarUserManagerHelper.java b/car-lib/src/android/car/user/CarUserManagerHelper.java
index c588547..ce50943 100644
--- a/car-lib/src/android/car/user/CarUserManagerHelper.java
+++ b/car-lib/src/android/car/user/CarUserManagerHelper.java
@@ -28,6 +28,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -62,6 +63,18 @@
private static final Set<String> DEFAULT_NON_ADMIN_RESTRICTIONS = Sets.newArraySet(
UserManager.DISALLOW_FACTORY_RESET
);
+ /**
+ * Default set of restrictions for Guest users.
+ */
+ private static final Set<String> DEFAULT_GUEST_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_FACTORY_RESET,
+ UserManager.DISALLOW_REMOVE_USER,
+ UserManager.DISALLOW_MODIFY_ACCOUNTS,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_UNINSTALL_APPS
+ );
private final Context mContext;
private final UserManager mUserManager;
@@ -184,7 +197,7 @@
/**
* Get user id for the initial user to boot into. This is only applicable for headless
- * user 0 model.
+ * system user model.
*
* <p>If failed to retrieve the id stored in global settings or the retrieved id does not
* exist on device, then return the user with smallest user id.
@@ -216,6 +229,19 @@
}
/**
+ * Sets default guest restrictions that will be applied every time a Guest user is created.
+ *
+ * <p> Restrictions are written to disk and persistent across boots.
+ */
+ public void initDefaultGuestRestrictions() {
+ Bundle defaultGuestRestrictions = new Bundle();
+ for (String restriction : DEFAULT_GUEST_RESTRICTIONS) {
+ defaultGuestRestrictions.putBoolean(restriction, true);
+ }
+ mUserManager.setDefaultGuestRestrictions(defaultGuestRestrictions);
+ }
+
+ /**
* Returns {@code true} if the system is in the headless user 0 model.
*
* @return {@boolean true} if headless system user.
@@ -339,6 +365,24 @@
}
/**
+ * Gets all users that are not guests.
+ *
+ * @return List of {@code UserInfo} for all users who are not guest users.
+ */
+ public List<UserInfo> getAllUsersExceptGuests() {
+ List<UserInfo> users = getAllUsers();
+
+ for (Iterator<UserInfo> iterator = users.iterator(); iterator.hasNext(); ) {
+ UserInfo userInfo = iterator.next();
+ if (userInfo.isGuest()) {
+ // Remove guests.
+ iterator.remove();
+ }
+ }
+ return users;
+ }
+
+ /**
* Get all the users except the one with userId passed in.
*
* @param userId of the user not to be returned.
@@ -376,6 +420,62 @@
return users;
}
+ /**
+ * Maximum number of users allowed on the device. This includes real users, managed profiles
+ * and restricted users, but excludes guests.
+ *
+ * <p> It excludes system user in headless system user model.
+ *
+ * @return Maximum number of users that can be present on the device.
+ */
+ public int getMaxSupportedUsers() {
+ if (isHeadlessSystemUser()) {
+ return UserManager.getMaxSupportedUsers() - 1;
+ }
+ return UserManager.getMaxSupportedUsers();
+ }
+
+ /**
+ * Get the maximum number of real (non-guest, non-managed profile) users that can be created on
+ * the device. This is a dynamic value and it decreases with the increase of the number of
+ * managed profiles on the device.
+ *
+ * <p> It excludes system user in headless system user model.
+ *
+ * @return Maximum number of real users that can be created.
+ */
+ public int getMaxSupportedRealUsers() {
+ return getMaxSupportedUsers() - getManagedProfilesCount();
+ }
+
+ /**
+ * Returns true if the maximum number of users on the device has been reached, false otherwise.
+ */
+ public boolean isUserLimitReached() {
+ int countNonGuestUsers = getAllUsersExceptGuests().size();
+ int maxSupportedUsers = getMaxSupportedUsers();
+
+ if (countNonGuestUsers > maxSupportedUsers) {
+ Log.e(TAG, "There are more users on the device than allowed.");
+ return true;
+ }
+
+ return getAllUsersExceptGuests().size() == maxSupportedUsers;
+ }
+
+ private int getManagedProfilesCount() {
+ List<UserInfo> users = getAllUsers();
+
+ // Count all users that are managed profiles of another user.
+ int managedProfilesCount = 0;
+ for (UserInfo user : users) {
+ if (user.isManagedProfile()) {
+ managedProfilesCount++;
+ }
+ }
+ return managedProfilesCount;
+ }
+
// User information accessors
/**
diff --git a/service/src/com/android/car/CarInputService.java b/service/src/com/android/car/CarInputService.java
index 1f67259..2b97221 100644
--- a/service/src/com/android/car/CarInputService.java
+++ b/service/src/com/android/car/CarInputService.java
@@ -16,7 +16,9 @@
package com.android.car;
import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
+import android.app.ActivityManager;
import android.car.input.CarInputHandlingService;
import android.car.input.CarInputHandlingService.InputFilter;
import android.car.input.ICarInputListener;
@@ -30,19 +32,18 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.CallLog.Calls;
-import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import com.android.car.hal.InputHalService;
-import com.android.car.hal.VehicleHal;
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -83,12 +84,30 @@
}
}
+ private IVoiceInteractionSessionShowCallback mShowCallback =
+ new IVoiceInteractionSessionShowCallback.Stub() {
+ @Override
+ public void onFailed() {
+ Log.w(CarLog.TAG_INPUT, "Failed to show VoiceInteractionSession");
+ }
+
+ @Override
+ public void onShown() {
+ if (DBG) {
+ Log.d(CarLog.TAG_INPUT, "IVoiceInteractionSessionShowCallback onShown()");
+ }
+ }
+ };
+
private static final boolean DBG = false;
+ private static final String EXTRA_CAR_PUSH_TO_TALK =
+ "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK";
private final Context mContext;
private final InputHalService mInputHalService;
private final TelecomManager mTelecomManager;
private final InputManager mInputManager;
+ private final AssistUtils mAssistUtils;
private KeyEventListener mVoiceAssistantKeyListener;
private KeyEventListener mLongVoiceAssistantKeyListener;
@@ -153,6 +172,7 @@
mInputHalService = inputHalService;
mTelecomManager = context.getSystemService(TelecomManager.class);
mInputManager = context.getSystemService(InputManager.class);
+ mAssistUtils = new AssistUtils(context);
}
private synchronized void setHandledKeys(InputFilter[] handledKeys) {
@@ -324,10 +344,18 @@
}
private void launchDefaultVoiceAssistantHandler() {
- Log.i(CarLog.TAG_INPUT, "voice key, launch default intent");
- Intent voiceIntent =
- new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
- mContext.startActivityAsUser(voiceIntent, null, UserHandle.CURRENT_OR_SELF);
+ Log.i(CarLog.TAG_INPUT, "voice key, invoke AssistUtils");
+
+ if (mAssistUtils.getAssistComponentForUser(ActivityManager.getCurrentUser()) == null) {
+ Log.w(CarLog.TAG_INPUT, "Unable to retrieve assist component for current user");
+ return;
+ }
+
+ final Bundle args = new Bundle();
+ args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true);
+
+ mAssistUtils.showSessionForActiveService(args,
+ SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, null /*activityToken*/);
}
private void handleInstrumentClusterKey(KeyEvent event) {
diff --git a/service/src/com/android/car/CarNightService.java b/service/src/com/android/car/CarNightService.java
index e43ed3d..dcf75b5 100644
--- a/service/src/com/android/car/CarNightService.java
+++ b/service/src/com/android/car/CarNightService.java
@@ -72,23 +72,27 @@
CarPropertyValue value = event.getCarPropertyValue();
if (value.getPropertyId() == VehicleProperty.NIGHT_MODE) {
boolean nightMode = (Boolean) value.getValue();
- if (nightMode) {
- mNightSetting = UiModeManager.MODE_NIGHT_YES;
- if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent NIGHT");
- } else {
- mNightSetting = UiModeManager.MODE_NIGHT_NO;
- if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent DAY");
- }
- if (mUiModeManager != null && (mForcedMode == FORCED_SENSOR_MODE)) {
- mUiModeManager.setNightMode(mNightSetting);
- if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent APPLIED");
- } else {
- if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent IGNORED");
- }
+ setNightMode(nightMode);
}
}
}
+ private synchronized void setNightMode(boolean nightMode) {
+ if (nightMode) {
+ mNightSetting = UiModeManager.MODE_NIGHT_YES;
+ if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent NIGHT");
+ } else {
+ mNightSetting = UiModeManager.MODE_NIGHT_NO;
+ if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent DAY");
+ }
+ if (mUiModeManager != null && (mForcedMode == FORCED_SENSOR_MODE)) {
+ mUiModeManager.setNightMode(mNightSetting);
+ if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent APPLIED");
+ } else {
+ if (DBG) Log.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent IGNORED");
+ }
+ }
+
public synchronized int forceDayNightMode(@DayNightSensorMode int mode) {
if (mUiModeManager == null) {
return -1;
@@ -131,6 +135,15 @@
}
mCarPropertyService.registerListener(VehicleProperty.NIGHT_MODE, 0,
mICarPropertyEventListener);
+ CarPropertyValue propertyValue = mCarPropertyService.getProperty(
+ VehicleProperty.NIGHT_MODE, 0);
+ if (propertyValue != null && propertyValue.getTimestamp() != 0) {
+ setNightMode((Boolean) propertyValue.getValue());
+ } else {
+ Log.w(CarLog.TAG_SENSOR, "Failed to get value of NIGHT_MODE");
+ // Initial in Night Mode
+ setNightMode(true);
+ }
}
@Override
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 3898e74..c63a2fd 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -93,6 +93,7 @@
if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) {
if (mCarUserManagerHelper.getAllUsers().size() == 0) {
setSystemUserRestrictions();
+ mCarUserManagerHelper.initDefaultGuestRestrictions();
// On very first boot, create an admin user and switch to that user.
UserInfo admin = mCarUserManagerHelper.createNewAdminUser(OWNER_NAME);
mCarUserManagerHelper.switchToUser(admin);
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index bc89ad4..6e8e80f 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -42,6 +42,8 @@
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
<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 0000000..15422b1
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/connectivity_fragment.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <ListView
+ android:id="@+id/networks"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ </ListView>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp">
+ <Button android:id="@+id/networksRefresh"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Refresh"/>
+ <Button android:id="@+id/networkRequestOemPaid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Request OEM-paid"/>
+ <Button android:id="@+id/networkRequestEth1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Request eth1"/>
+ <Button android:id="@+id/networkReleaseNetwork"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Release Request"/>
+ </LinearLayout>
+
+</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 0000000..f517913
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/list_item.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:paddingTop="2dip"
+ android:paddingBottom="3dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textSize="24sp" />
\ No newline at end of file
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 7837f2e..caca03a 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.property.CarPropertyManager;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Handler;
import android.support.car.Car;
import android.support.car.CarAppFocusManager;
import android.support.car.CarConnectionCallback;
@@ -42,6 +44,7 @@
import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment;
import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment;
+import com.google.android.car.kitchensink.connectivity.ConnectivityFragment;
import com.google.android.car.kitchensink.cube.CubesTestFragment;
import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment;
import com.google.android.car.kitchensink.displayinfo.DisplayInfoFragment;
@@ -167,6 +170,7 @@
startActivity(intent);
});
add("activity view", ActivityViewTestFragment.class);
+ add("connectivity", ConnectivityFragment.class);
add("quit", KitchenSinkActivity.this::finish);
}
@@ -183,6 +187,7 @@
private CarPropertyManager mPropertyManager;
private CarSensorManager mSensorManager;
private CarAppFocusManager mCarAppFocusManager;
+ private Object mPropertyManagerReady = new Object();
public CarHvacManager getHvacManager() {
return mHvacManager;
@@ -212,12 +217,20 @@
setMainContent(R.layout.kitchen_content);
// Connection to Car Service does not work for non-automotive yet.
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- mCarApi = Car.createCar(this, mCarConnectionCallback);
- mCarApi.connect();
+ initCarApi();
}
Log.i(TAG, "onCreate");
}
+ private void initCarApi() {
+ if (mCarApi != null && mCarApi.isConnected()) {
+ mCarApi.disconnect();
+ mCarApi = null;
+ }
+ mCarApi = Car.createCar(this, mCarConnectionCallback);
+ mCarApi.connect();
+ }
+
@Override
protected void onStart() {
super.onStart();
@@ -268,18 +281,22 @@
@Override
public void onConnected(Car car) {
Log.d(TAG, "Connected to Car Service");
- try {
- mHvacManager = (CarHvacManager) mCarApi.getCarManager(android.car.Car.HVAC_SERVICE);
- mPowerManager = (CarPowerManager) mCarApi.getCarManager(
- android.car.Car.POWER_SERVICE);
- mPropertyManager = (CarPropertyManager) mCarApi.getCarManager(
- android.car.Car.PROPERTY_SERVICE);
- mSensorManager = (CarSensorManager) mCarApi.getCarManager(
- android.car.Car.SENSOR_SERVICE);
- mCarAppFocusManager =
- (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
+ synchronized (mPropertyManagerReady) {
+ try {
+ mHvacManager = (CarHvacManager) mCarApi.getCarManager(
+ android.car.Car.HVAC_SERVICE);
+ mPowerManager = (CarPowerManager) mCarApi.getCarManager(
+ android.car.Car.POWER_SERVICE);
+ mPropertyManager = (CarPropertyManager) mCarApi.getCarManager(
+ android.car.Car.PROPERTY_SERVICE);
+ mSensorManager = (CarSensorManager) mCarApi.getCarManager(
+ android.car.Car.SENSOR_SERVICE);
+ mCarAppFocusManager =
+ (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
+ mPropertyManagerReady.notifyAll();
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
}
}
@@ -322,4 +339,29 @@
getDrawerController().closeDrawer();
}
}
+
+ // Use AsyncTask to refresh Car*Manager after car service connected
+ public void requestRefreshManager(final Runnable r, final Handler h) {
+ final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... unused) {
+ synchronized (mPropertyManagerReady) {
+ while (!mCarApi.isConnected()) {
+ try {
+ mPropertyManagerReady.wait();
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void unused) {
+ h.post(r);
+ }
+ };
+ task.execute();
+ }
}
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 0000000..0ffa6bf
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/connectivity/ConnectivityFragment.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.connectivity;
+
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.ArrayList;
+
+@SuppressLint("SetTextI18n")
+public class ConnectivityFragment extends Fragment {
+ private static final String TAG = ConnectivityFragment.class.getSimpleName();
+
+ private final Handler mHandler = new Handler();
+ private final ArrayList<String> mNetworks = new ArrayList<>();
+
+ private ConnectivityManager mConnectivityManager;
+ private ArrayAdapter<String> mNetworksAdapter;
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ showToast("onAvailable, netId: " + network);
+ refreshNetworks();
+ }
+
+ @Override
+ public void onLost(Network network) {
+ showToast("onLost, netId: " + network);
+ refreshNetworks();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mConnectivityManager = getActivity().getSystemService(ConnectivityManager.class);
+
+ mConnectivityManager.addDefaultNetworkActiveListener(() -> refreshNetworks());
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.connectivity_fragment, container, false);
+
+ ListView networksView = view.findViewById(R.id.networks);
+ mNetworksAdapter = new ArrayAdapter<>(getActivity(), R.layout.list_item, mNetworks);
+ networksView.setAdapter(mNetworksAdapter);
+
+ setClickAction(view, R.id.networksRefresh, this::refreshNetworks);
+ setClickAction(view, R.id.networkRequestOemPaid, this::requestOemPaid);
+ setClickAction(view, R.id.networkRequestEth1, this::requestEth1);
+ setClickAction(view, R.id.networkReleaseNetwork, this::releaseNetworkRequest);
+
+ return view;
+ }
+
+ private void releaseNetworkRequest() {
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ showToast("Release request sent");
+ }
+
+ private void requestEth1() {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
+ .setNetworkSpecifier("eth1")
+ .build();
+ mConnectivityManager.requestNetwork(request, mNetworkCallback, mHandler);
+ }
+
+ private void requestOemPaid() {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)
+ .build();
+
+ mConnectivityManager.requestNetwork(request, mNetworkCallback, mHandler);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refreshNetworks();
+ }
+
+ private void setClickAction(View view, int id, Runnable action) {
+ view.findViewById(id).setOnClickListener(v -> action.run());
+ }
+
+ private void refreshNetworks() {
+ mNetworks.clear();
+
+ for (Network network : mConnectivityManager.getAllNetworks()) {
+ boolean isDefault = sameNetworkId(network, mConnectivityManager.getActiveNetwork());
+ NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(network);
+ boolean isOemPaid = nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID);
+ boolean isInternet = nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
+
+ mNetworks.add("netId: " + network.netId
+ + (isInternet ? " [INTERNET]" : "")
+ + (isDefault ? " [DEFAULT]" : "")
+ + (isOemPaid ? " [OEM-paid]" : "") + nc + " " + networkInfo);
+ }
+
+ mNetworksAdapter.notifyDataSetChanged();
+ }
+
+ private void showToast(String text) {
+ Log.d(TAG, "showToast: " + text);
+ Toast.makeText(getContext(), text, Toast.LENGTH_LONG).show();
+ }
+
+ private static boolean sameNetworkId(Network net1, Network net2) {
+ return net1 != null && net2 != null && net1.netId == net2.netId;
+
+ }
+}
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 a1f8e1d..c7b80e8 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.hardware.automotive.vehicle.V2_0.VehicleAreaSeat;
import android.hardware.automotive.vehicle.V2_0.VehicleAreaWindow;
import android.os.Bundle;
+import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -76,6 +77,8 @@
private int mZoneForSetTempP;
private int mZoneForFanSpeed;
private int mZoneForFanPosition;
+ private List<CarPropertyConfig> mCarPropertyConfigs;
+ private View mHvacView;
private final CarHvacManager.CarHvacEventCallback mHvacCallback =
new CarHvacManager.CarHvacEventCallback () {
@@ -171,13 +174,9 @@
@Override
public void onCreate(Bundle savedInstanceState) {
- mCarHvacManager = ((KitchenSinkActivity)getActivity()).getHvacManager();
+
super.onCreate(savedInstanceState);
- try {
- mCarHvacManager.registerCallback(mHvacCallback);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!");
- }
+
}
@Override
@@ -188,77 +187,85 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
- View v = inflater.inflate(R.layout.hvac_test, container, false);
+ mHvacView = inflater.inflate(R.layout.hvac_test, container, false);
+ final Runnable r = () -> {
+ mCarHvacManager = ((KitchenSinkActivity) getActivity()).getHvacManager();
+ try {
+ mCarHvacManager.registerCallback(mHvacCallback);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!");
+ }
+ try {
+ mCarPropertyConfigs = mCarHvacManager.getPropertyList();
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Failed to get list of properties", e);
+ mCarPropertyConfigs = new ArrayList<>();
+ }
+ for (CarPropertyConfig prop : mCarPropertyConfigs) {
+ int propId = prop.getPropertyId();
- List<CarPropertyConfig> props;
- try {
- props = mCarHvacManager.getPropertyList();
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to get list of properties", e);
- props = new ArrayList<>();
- }
+ if (DBG) {
+ Log.d(TAG, prop.toString());
+ }
- for(CarPropertyConfig prop : props) {
- int propId = prop.getPropertyId();
-
- if(DBG) {
- Log.d(TAG, prop.toString());
+ switch(propId) {
+ case CarHvacManager.ID_OUTSIDE_AIR_TEMP:
+ configureOutsideTemp(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_DUAL_ZONE_ON:
+ configureDualOn(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_AC_ON:
+ configureAcOn(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_FAN_DIRECTION:
+ configureFanPosition(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT:
+ configureFanSpeed(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_TEMP_SETPOINT:
+ configureTempSetpoint(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON:
+ configureAutoModeOn(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON:
+ configureRecircOn(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_MAX_AC_ON:
+ configureMaxAcOn(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_ZONED_MAX_DEFROST_ON:
+ configureMaxDefrostOn(mHvacView, prop);
+ break;
+ case CarHvacManager.ID_WINDOW_DEFROSTER_ON:
+ configureDefrosterOn(mHvacView, prop);
+ break;
+ default:
+ Log.w(TAG, "propertyId " + propId + " is not handled");
+ break;
+ }
}
- switch(propId) {
- case CarHvacManager.ID_OUTSIDE_AIR_TEMP:
- configureOutsideTemp(v, prop);
- break;
- case CarHvacManager.ID_ZONED_DUAL_ZONE_ON:
- configureDualOn(v, prop);
- break;
- case CarHvacManager.ID_ZONED_AC_ON:
- configureAcOn(v, prop);
- break;
- case CarHvacManager.ID_ZONED_FAN_DIRECTION:
- configureFanPosition(v, prop);
- break;
- case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT:
- configureFanSpeed(v, prop);
- break;
- case CarHvacManager.ID_ZONED_TEMP_SETPOINT:
- configureTempSetpoint(v, prop);
- break;
- case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON:
- configureAutoModeOn(v, prop);
- break;
- case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON:
- configureRecircOn(v, prop);
- break;
- case CarHvacManager.ID_ZONED_MAX_AC_ON:
- configureMaxAcOn(v, prop);
- break;
- case CarHvacManager.ID_ZONED_MAX_DEFROST_ON:
- configureMaxDefrostOn(v, prop);
- break;
- case CarHvacManager.ID_WINDOW_DEFROSTER_ON:
- configureDefrosterOn(v, prop);
- break;
- default:
- Log.w(TAG, "propertyId " + propId + " is not handled");
- break;
- }
- }
+ mTvFanSpeed = (TextView) mHvacView.findViewById(R.id.tvFanSpeed);
+ mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+ mTvDTemp = (TextView) mHvacView.findViewById(R.id.tvDTemp);
+ mTvDTemp.setText(String.valueOf(mCurDTemp));
+ mTvPTemp = (TextView) mHvacView.findViewById(R.id.tvPTemp);
+ mTvPTemp.setText(String.valueOf(mCurPTemp));
+ mTvOutsideTemp = (TextView) mHvacView.findViewById(R.id.tvOutsideTemp);
+ mTvOutsideTemp.setText("N/A");
+ };
- mTvFanSpeed = (TextView) v.findViewById(R.id.tvFanSpeed);
- mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
- mTvDTemp = (TextView) v.findViewById(R.id.tvDTemp);
- mTvDTemp.setText(String.valueOf(mCurDTemp));
- mTvPTemp = (TextView) v.findViewById(R.id.tvPTemp);
- mTvPTemp.setText(String.valueOf(mCurPTemp));
- mTvOutsideTemp = (TextView) v.findViewById(R.id.tvOutsideTemp);
- mTvOutsideTemp.setText("N/A");
+ ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
+ new Handler(getContext().getMainLooper()));
if(DBG) {
Log.d(TAG, "Starting HvacTestFragment");
}
- return v;
+ return mHvacView;
}
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 fed1fbd..9a6c2b9 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.hardware.power.CarPowerManager;
import android.content.Context;
import android.os.Bundle;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.support.v4.app.Fragment;
@@ -58,16 +59,20 @@
@Override
public void onCreate(Bundle savedInstanceState) {
- mCarPowerManager = ((KitchenSinkActivity)getActivity()).getPowerManager();
- mExecutor = new ThreadPerTaskExecutor();
+ final Runnable r = () -> {
+ mCarPowerManager = ((KitchenSinkActivity) getActivity()).getPowerManager();
+ mExecutor = new ThreadPerTaskExecutor();
+ try {
+ mCarPowerManager.setListener(mPowerListener, mExecutor);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "CarPowerManager listener was not cleared");
+ }
+ };
+ ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
+ new Handler(getContext().getMainLooper()));
super.onCreate(savedInstanceState);
- try {
- mCarPowerManager.setListener(mPowerListener, mExecutor);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!");
- } catch (IllegalStateException e) {
- Log.e(TAG, "CarPowerManager listener was not cleared");
- }
}
@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 fc6621a..ff1c402 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.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
import android.os.Bundle;
+import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -85,20 +86,23 @@
mPropertyId = view.findViewById(R.id.sPropertyId);
mScrollView = view.findViewById(R.id.svEventLog);
mSetValue = view.findViewById(R.id.etSetPropertyValue);
+ mActivity = (KitchenSinkActivity) getActivity();
- populateConfigList();
- mListView.setAdapter(new PropertyListAdapter(mPropInfo, mMgr, mEventLog, mScrollView,
- mActivity));
+ final Runnable r = () -> {
+ mMgr = mActivity.getPropertyManager();
+ populateConfigList();
+ mListView.setAdapter(new PropertyListAdapter(mPropInfo, mMgr, mEventLog, mScrollView,
+ mActivity));
- // Configure dropdown menu for propertyId spinner
- ArrayAdapter<PropertyInfo> adapter =
- new ArrayAdapter<PropertyInfo>(mActivity, android.R.layout.simple_spinner_item,
- mPropInfo);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mPropertyId.setAdapter(adapter);
- mPropertyId.setOnItemSelectedListener(this);
-
-
+ // Configure dropdown menu for propertyId spinner
+ ArrayAdapter<PropertyInfo> adapter =
+ new ArrayAdapter<PropertyInfo>(mActivity, android.R.layout.simple_spinner_item,
+ mPropInfo);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mPropertyId.setAdapter(adapter);
+ mPropertyId.setOnItemSelectedListener(this);
+ };
+ mActivity.requestRefreshManager(r, new Handler(getContext().getMainLooper()));
// Configure listeners for buttons
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 a6cc5ac..1440ff0 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 @@
View view = inflater.inflate(R.layout.sensors, container, false);
mActivity = (KitchenSinkActivity) getHost();
-
mSensorInfo = (TextView) view.findViewById(R.id.sensor_info);
mNaString = getContext().getString(R.string.sensor_na);
-
return view;
}
@Override
public void onResume() {
super.onResume();
- initPermissions();
+ final Runnable r = () -> {
+ initPermissions();
+ };
+ ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
+ new Handler(getContext().getMainLooper()));
}
@Override
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 8fc1eea..7f53da5 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarUserManagerHelperTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarUserManagerHelperTest.java
@@ -34,6 +34,7 @@
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -51,7 +52,6 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -128,15 +128,8 @@
UserInfo otherUser2 = createUserInfoForId(11);
UserInfo otherUser3 = createUserInfoForId(12);
- List<UserInfo> testUsers = new ArrayList<>();
- testUsers.add(mSystemUser);
- testUsers.add(otherUser1);
- testUsers.add(otherUser2);
- testUsers.add(otherUser3);
+ mockGetUsers(mSystemUser, otherUser1, otherUser2, otherUser3);
- doReturn(testUsers).when(mUserManager).getUsers(true);
-
- assertThat(mCarUserManagerHelper.getAllUsers()).hasSize(3);
assertThat(mCarUserManagerHelper.getAllUsers())
.containsExactly(otherUser1, otherUser2, otherUser3);
}
@@ -147,15 +140,9 @@
UserInfo user1 = createUserInfoForId(mForegroundUserId + 1);
UserInfo user2 = createUserInfoForId(mForegroundUserId + 2);
- List<UserInfo> testUsers = Arrays.asList(mForegroundUser, user1, user2);
-
- doReturn(new ArrayList<>(testUsers)).when(mUserManager).getUsers(true);
-
- // Should return all 3 users.
- assertThat(mCarUserManagerHelper.getAllUsers()).hasSize(3);
+ mockGetUsers(mForegroundUser, user1, user2);
// Should return all non-foreground users.
- assertThat(mCarUserManagerHelper.getAllSwitchableUsers()).hasSize(2);
assertThat(mCarUserManagerHelper.getAllSwitchableUsers()).containsExactly(user1, user2);
}
@@ -170,19 +157,41 @@
UserInfo user4 = new UserInfo(
/* id= */mForegroundUserId + 3, /* name = */ "user4", UserInfo.FLAG_EPHEMERAL);
- List<UserInfo> testUsers = Arrays.asList(user1, user2, user3, user4);
-
- doReturn(new ArrayList<>(testUsers)).when(mUserManager).getUsers(true);
-
- // Should return all 4 users.
- assertThat(mCarUserManagerHelper.getAllUsers()).hasSize(4);
+ mockGetUsers(user1, user2, user3, user4);
// Should return all non-ephemeral users.
- assertThat(mCarUserManagerHelper.getAllPersistentUsers()).hasSize(2);
assertThat(mCarUserManagerHelper.getAllPersistentUsers()).containsExactly(user1, user2);
}
@Test
+ public void testGetAllAdminUsers() {
+ // Create two admin, and two non-admin users.
+ UserInfo user1 = new UserInfo(/* id= */ 10, /* name = */ "user10", UserInfo.FLAG_ADMIN);
+ UserInfo user2 = createUserInfoForId(11);
+ UserInfo user3 = createUserInfoForId(12);
+ UserInfo user4 = new UserInfo(/* id= */ 13, /* name = */ "user13", UserInfo.FLAG_ADMIN);
+
+ mockGetUsers(user1, user2, user3, user4);
+
+ // Should return only admin users.
+ assertThat(mCarUserManagerHelper.getAllAdminUsers()).containsExactly(user1, user4);
+ }
+
+ @Test
+ public void testGetAllUsersExceptGuests() {
+ // Create two users and a guest user.
+ UserInfo user1 = createUserInfoForId(10);
+ UserInfo user2 = createUserInfoForId(12);
+ UserInfo user3 = new UserInfo(/* id= */ 13, /* name = */ "user13", UserInfo.FLAG_GUEST);
+
+ mockGetUsers(user1, user2, user3);
+
+ // Should not return guests.
+ assertThat(mCarUserManagerHelper.getAllUsersExceptGuests())
+ .containsExactly(user1, user2);
+ }
+
+ @Test
public void testUserCanBeRemoved() {
UserInfo testInfo = new UserInfo();
@@ -256,6 +265,99 @@
}
@Test
+ public void testGetMaxSupportedUsers() {
+ SystemProperties.set("fw.max_users", "11");
+
+ assertThat(mCarUserManagerHelper.getMaxSupportedUsers()).isEqualTo(11);
+
+ // In headless user 0 model, we want to exclude the system user.
+ SystemProperties.set("android.car.systemuser.headless", "true");
+ assertThat(mCarUserManagerHelper.getMaxSupportedUsers()).isEqualTo(10);
+ }
+
+ @Test
+ public void testGetMaxSupportedRealUsers() {
+ SystemProperties.set("fw.max_users", "7");
+
+ // Create three managed profiles, and two normal users.
+ UserInfo user1 = createUserInfoForId(10);
+ UserInfo user2 =
+ new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user3 =
+ new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user4 = createUserInfoForId(13);
+ UserInfo user5 =
+ new UserInfo(/* id= */ 14, /* name = */ "user14", UserInfo.FLAG_MANAGED_PROFILE);
+
+ mockGetUsers(user1, user2, user3, user4, user5);
+
+ // Max users - # managed profiles.
+ assertThat(mCarUserManagerHelper.getMaxSupportedRealUsers()).isEqualTo(4);
+ }
+
+ @Test
+ public void testIsUserLimitReached() {
+ UserInfo user1 = createUserInfoForId(10);
+ UserInfo user2 =
+ new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user3 =
+ new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user4 = createUserInfoForId(13);
+
+ mockGetUsers(user1, user2, user3, user4);
+
+ SystemProperties.set("fw.max_users", "5");
+ assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
+
+ SystemProperties.set("fw.max_users", "4");
+ assertThat(mCarUserManagerHelper.isUserLimitReached()).isTrue();
+ }
+
+ @Test
+ public void testHeadlessSystemUser_IsUserLimitReached() {
+ SystemProperties.set("android.car.systemuser.headless", "true");
+ UserInfo user1 = createUserInfoForId(10);
+ UserInfo user2 =
+ new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user3 =
+ new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user4 = createUserInfoForId(13);
+
+ mockGetUsers(mSystemUser, user1, user2, user3, user4);
+
+ SystemProperties.set("fw.max_users", "6");
+ assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
+
+ SystemProperties.set("fw.max_users", "5");
+ assertThat(mCarUserManagerHelper.isUserLimitReached()).isTrue();
+ }
+
+ @Test
+ public void testIsUserLimitReachedIgnoresGuests() {
+ SystemProperties.set("fw.max_users", "5");
+
+ UserInfo user1 = createUserInfoForId(10);
+ UserInfo user2 =
+ new UserInfo(/* id= */ 11, /* name = */ "user11", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user3 =
+ new UserInfo(/* id= */ 12, /* name = */ "user12", UserInfo.FLAG_MANAGED_PROFILE);
+ UserInfo user4 = createUserInfoForId(13);
+ UserInfo user5 = new UserInfo(/* id= */ 14, /* name = */ "user14", UserInfo.FLAG_GUEST);
+ UserInfo user6 = createUserInfoForId(15);
+
+ mockGetUsers(user1, user2, user3, user4);
+ assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
+
+ // Add guest user. Verify it doesn't affect the limit.
+ mockGetUsers(user1, user2, user3, user4, user5);
+ assertThat(mCarUserManagerHelper.isUserLimitReached()).isFalse();
+
+ // Add normal user. Limit is reached
+ mockGetUsers(user1, user2, user3, user4, user5, user6);
+ assertThat(mCarUserManagerHelper.isUserLimitReached()).isTrue();
+ }
+
+ @Test
public void testCreateNewAdminUser() {
// Make sure current user is admin, since only admins can create other admins.
doReturn(true).when(mUserManager).isAdminUser();
@@ -366,10 +468,7 @@
assertThat(mCarUserManagerHelper.removeUser(mSystemUser, mGuestUserName)).isFalse();
UserInfo adminInfo = new UserInfo(/* id= */10, "admin", UserInfo.FLAG_ADMIN);
- List<UserInfo> users = new ArrayList<UserInfo>();
- users.add(adminInfo);
-
- doReturn(users).when(mUserManager).getUsers(true);
+ mockGetUsers(adminInfo);
assertThat(mCarUserManagerHelper.removeUser(adminInfo, mGuestUserName))
.isEqualTo(false);
@@ -468,6 +567,26 @@
}
@Test
+ public void testDefaultGuestRestrictions() {
+ int guestRestrictionsExpectedCount = 7;
+
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ mCarUserManagerHelper.initDefaultGuestRestrictions();
+
+ verify(mUserManager).setDefaultGuestRestrictions(bundleCaptor.capture());
+ Bundle guestRestrictions = bundleCaptor.getValue();
+
+ assertThat(guestRestrictions.keySet()).hasSize(guestRestrictionsExpectedCount);
+ assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_FACTORY_RESET)).isTrue();
+ assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_REMOVE_USER)).isTrue();
+ assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)).isTrue();
+ assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)).isTrue();
+ assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_SMS)).isTrue();
+ assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_APPS)).isTrue();
+ assertThat(guestRestrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS)).isTrue();
+ }
+
+ @Test
public void testAssigningAdminPrivilegesRemovesNonAdminRestrictions() {
int testUserId = 30;
boolean restrictionEnabled = false;
@@ -631,15 +750,9 @@
UserInfo otherUser2 = createUserInfoForId(lastActiveUserId - 1);
UserInfo otherUser3 = createUserInfoForId(lastActiveUserId);
- List<UserInfo> testUsers = new ArrayList<>();
- testUsers.add(mSystemUser);
- testUsers.add(otherUser1);
- testUsers.add(otherUser2);
- testUsers.add(otherUser3);
-
mCarUserManagerHelper.setLastActiveUser(
lastActiveUserId, /* skipGlobalSettings= */ true);
- doReturn(testUsers).when(mUserManager).getUsers(true);
+ mockGetUsers(mSystemUser, otherUser1, otherUser2, otherUser3);
assertThat(mCarUserManagerHelper.getInitialUser()).isEqualTo(lastActiveUserId);
}
@@ -652,14 +765,9 @@
UserInfo otherUser1 = createUserInfoForId(lastActiveUserId - 2);
UserInfo otherUser2 = createUserInfoForId(lastActiveUserId - 1);
- List<UserInfo> testUsers = new ArrayList<>();
- testUsers.add(mSystemUser);
- testUsers.add(otherUser1);
- testUsers.add(otherUser2);
-
mCarUserManagerHelper.setLastActiveUser(
lastActiveUserId, /* skipGlobalSettings= */ true);
- doReturn(testUsers).when(mUserManager).getUsers(true);
+ mockGetUsers(mSystemUser, otherUser1, otherUser2);
assertThat(mCarUserManagerHelper.getInitialUser()).isEqualTo(lastActiveUserId - 2);
}
@@ -669,4 +777,12 @@
userInfo.id = id;
return userInfo;
}
+
+ private void mockGetUsers(UserInfo... users) {
+ List<UserInfo> testUsers = new ArrayList<>();
+ for (UserInfo user: users) {
+ testUsers.add(user);
+ }
+ doReturn(testUsers).when(mUserManager).getUsers(true);
+ }
}
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 710b847..48d447b 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
@@ -32,9 +32,6 @@
import android.os.UserManager;
import android.support.test.runner.AndroidJUnit4;
-import java.util.ArrayList;
-import java.util.List;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,6 +39,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class contains unit tests for the {@link CarUserService}.
*
@@ -71,12 +71,14 @@
* Initialize all of the objects with the @Mock annotation.
*/
@Before
- public void setUp() throws Exception {
+ public void setUpMocks() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(mApplicationContext).when(mMockContext).getApplicationContext();
doReturn(mLocationManager).when(mMockContext).getSystemService(Context.LOCATION_SERVICE);
mCarUserService = new CarUserService(mMockContext, mCarUserManagerHelper);
+
+ doReturn(new ArrayList<>()).when(mCarUserManagerHelper).getAllUsers();
}
/**
@@ -109,13 +111,7 @@
*/
@Test
public void testStartsSecondaryAdminUserOnFirstRun() {
- List<UserInfo> users = new ArrayList<>();
-
- int adminUserId = 10;
- UserInfo admin = new UserInfo(adminUserId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
-
- doReturn(users).when(mCarUserManagerHelper).getAllUsers();
- doReturn(admin).when(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
+ UserInfo admin = mockAdmin(/* adminId= */ 10);
mCarUserService.onReceive(mMockContext,
new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
@@ -129,16 +125,12 @@
*/
@Test
public void testDisableModifyAccountsForSystemUserOnFirstRun() {
- List<UserInfo> users = new ArrayList<>();
-
+ // Mock system user.
UserInfo systemUser = new UserInfo();
systemUser.id = UserHandle.USER_SYSTEM;
- int adminUserId = 10;
- UserInfo admin = new UserInfo(adminUserId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
-
- doReturn(users).when(mCarUserManagerHelper).getAllUsers();
doReturn(systemUser).when(mCarUserManagerHelper).getSystemUserInfo();
- doReturn(admin).when(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
+
+ mockAdmin(10);
mCarUserService.onReceive(mMockContext,
new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
@@ -152,22 +144,13 @@
*/
@Test
public void testDisableLocationForSystemUserOnFirstRun() {
- List<UserInfo> users = new ArrayList<>();
-
- UserInfo systemUser = new UserInfo();
- systemUser.id = UserHandle.USER_SYSTEM;
- int adminUserId = 10;
- UserInfo admin = new UserInfo(adminUserId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
-
- doReturn(users).when(mCarUserManagerHelper).getAllUsers();
- doReturn(systemUser).when(mCarUserManagerHelper).getSystemUserInfo();
- doReturn(admin).when(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
+ mockAdmin(/* adminId= */ 10);
mCarUserService.onReceive(mMockContext,
new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
- verify(mLocationManager)
- .setLocationEnabledForUser(/* enabled= */ false, UserHandle.of(systemUser.id));
+ verify(mLocationManager).setLocationEnabledForUser(
+ /* enabled= */ false, UserHandle.of(UserHandle.USER_SYSTEM));
}
/**
@@ -176,21 +159,13 @@
*/
@Test
public void testUpdateLastActiveUserOnFirstRun() {
- List<UserInfo> users = new ArrayList<>();
-
- UserInfo systemUser = new UserInfo();
- systemUser.id = UserHandle.USER_SYSTEM;
- int adminUserId = 10;
- UserInfo admin = new UserInfo(adminUserId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
-
- doReturn(users).when(mCarUserManagerHelper).getAllUsers();
- doReturn(admin).when(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
+ UserInfo admin = mockAdmin(/* adminId= */ 10);
mCarUserService.onReceive(mMockContext,
new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
verify(mCarUserManagerHelper)
- .setLastActiveUser(adminUserId, /* skipGlobalSetting= */ false);
+ .setLastActiveUser(admin.id, /* skipGlobalSetting= */ false);
}
/**
@@ -236,4 +211,23 @@
verify(mCarUserManagerHelper).setLastActiveUser(
lastActiveUserId, /* skipGlobalSetting= */ false);
}
+
+ /**
+ * Test that the {@link CarUserService} sets default guest restrictions on first boot.
+ */
+ @Test
+ public void testInitializeGuestRestrictionsOnFirstRun() {
+ mockAdmin(/* adminId= */ 10);
+
+ mCarUserService.onReceive(mMockContext,
+ new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
+
+ verify(mCarUserManagerHelper).initDefaultGuestRestrictions();
+ }
+
+ private UserInfo mockAdmin(int adminId) {
+ UserInfo admin = new UserInfo(adminId, CarUserService.OWNER_NAME, UserInfo.FLAG_ADMIN);
+ doReturn(admin).when(mCarUserManagerHelper).createNewAdminUser(CarUserService.OWNER_NAME);
+ return admin;
+ }
}
diff --git a/tests/robotests/src/com/android/car/users/CarUserManagerHelperRoboTest.java b/tests/robotests/src/com/android/car/users/CarUserManagerHelperRoboTest.java
index 9c39445..fcfd6dc 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 @@
}
@Test
- public void testGetAllUsersExcludesForegroundUser() {
+ public void testGetAllUsersExceptForegroundUser() {
ShadowActivityManager.setCurrentUser(11);
ShadowUserManager userManager = ShadowUserManager.getShadow();