Add user 0 unlock monitoring to postpone user 0 data access
- Car service and services launched from it are starting while user 0 data is not ready.
- Access to user0 data should be postponed until user0 is unlocked.
- More changes to postpone access will be added as separate CL.
Only instrument cluster service is changed as an example test case.
Bug: 124223557
Test: boot up and check crash / logs for data access
Change-Id: I7e610792742f0ad7b93b3bcf4caedb4f0c8b6d17
diff --git a/service/src/com/android/car/CarLocalServices.java b/service/src/com/android/car/CarLocalServices.java
new file mode 100644
index 0000000..2c619af
--- /dev/null
+++ b/service/src/com/android/car/CarLocalServices.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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.android.car;
+
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Copy of frameworks/base/core/java/com/android/server/LocalServices.java
+ * This is for accessing other car service components.
+ */
+public class CarLocalServices {
+ private CarLocalServices() {}
+
+ private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
+ new ArrayMap<Class<?>, Object>();
+
+ /**
+ * Returns a local service instance that implements the specified interface.
+ *
+ * @param type The type of service.
+ * @return The service object.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T getService(Class<T> type) {
+ synchronized (sLocalServiceObjects) {
+ return (T) sLocalServiceObjects.get(type);
+ }
+ }
+
+ /**
+ * Adds a service instance of the specified interface to the global registry of local services.
+ */
+ public static <T> void addService(Class<T> type, T service) {
+ synchronized (sLocalServiceObjects) {
+ if (sLocalServiceObjects.containsKey(type)) {
+ throw new IllegalStateException("Overriding service registration");
+ }
+ sLocalServiceObjects.put(type, service);
+ }
+ }
+
+ /**
+ * Remove a service instance, must be only used in tests.
+ */
+ @VisibleForTesting
+ public static <T> void removeServiceForTest(Class<T> type) {
+ synchronized (sLocalServiceObjects) {
+ sLocalServiceObjects.remove(type);
+ }
+ }
+
+ /**
+ * Remove all registered services. Should be called when car service restarts.
+ */
+ public static void removeAllServices() {
+ synchronized (sLocalServiceObjects) {
+ sLocalServiceObjects.clear();
+ }
+ }
+}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index e2176d3..e02c65c 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -86,9 +86,8 @@
private final CarStorageMonitoringService mCarStorageMonitoringService;
private final CarConfigurationService mCarConfigurationService;
private final CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
-
private final CarUserManagerHelper mUserManagerHelper;
- private CarUserService mCarUserService;
+ private final CarUserService mCarUserService;
private final VmsClientManager mVmsClientManager;
private final VmsSubscriberService mVmsSubscriberService;
private final VmsPublisherService mVmsPublisherService;
@@ -116,6 +115,7 @@
mHal = new VehicleHal(vehicle);
mVehicleInterfaceName = vehicleInterfaceName;
mUserManagerHelper = new CarUserManagerHelper(serviceContext);
+ mCarUserService = new CarUserService(serviceContext, mUserManagerHelper);
mSystemActivityMonitoringService = new SystemActivityMonitoringService(serviceContext);
mCarPowerManagementService = new CarPowerManagementService(mContext, mHal.getPowerHal(),
systemInterface);
@@ -153,8 +153,11 @@
mContext, mCarPropertyService, mUserManagerHelper);
mCarTrustAgentEnrollmentService = new CarTrustAgentEnrollmentService(serviceContext);
+ CarLocalServices.addService(CarUserService.class, mCarUserService);
+
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>();
+ allServices.add(mCarUserService);
allServices.add(mSystemActivityMonitoringService);
allServices.add(mCarPowerManagementService);
allServices.add(mCarPropertyService);
@@ -178,9 +181,6 @@
allServices.add(mVmsSubscriberService);
allServices.add(mVmsPublisherService);
allServices.add(mCarTrustAgentEnrollmentService);
- if (mUserManagerHelper.isHeadlessSystemUser()) {
- allServices.add(new CarUserService(serviceContext, mUserManagerHelper));
- }
allServices.add(mCarLocationService);
mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
}
@@ -204,6 +204,7 @@
mAllServices[i].release();
}
mHal.release();
+ CarLocalServices.removeAllServices();
}
void vehicleHalReconnected(IVehicle vehicle) {
@@ -226,6 +227,15 @@
}
@Override
+ public void setUserLockStatus(int userHandle, int unlocked) {
+ int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException("Only allowed from system");
+ }
+ mCarUserService.setUserLockStatus(userHandle, unlocked == 1);
+ }
+
+ @Override
public IBinder getCarService(String serviceName) {
switch (serviceName) {
case Car.AUDIO_SERVICE:
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index b491350..fd16da5 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -39,9 +39,11 @@
import com.android.car.AppFocusService.FocusOwnershipCallback;
import com.android.car.CarInputService;
import com.android.car.CarInputService.KeyEventListener;
+import com.android.car.CarLocalServices;
import com.android.car.CarLog;
import com.android.car.CarServiceBase;
import com.android.car.R;
+import com.android.car.user.CarUserService;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
@@ -133,7 +135,11 @@
mAppFocusService.registerContextOwnerChangedCallback(this /* FocusOwnershipCallback */);
mCarInputService.setInstrumentClusterKeyListener(this /* KeyEventListener */);
- mRendererBound = bindInstrumentClusterRendererService();
+ // TODO(b/124246323) Start earlier once data storage for cluster is clarified
+ // for early boot.
+ CarLocalServices.getService(CarUserService.class).runOnUser0Unlock(() -> {
+ mRendererBound = bindInstrumentClusterRendererService();
+ });
}
@Override
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 72e313f..760b435 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -30,8 +30,10 @@
import android.util.Log;
import com.android.car.CarServiceBase;
+import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* User service for cars. Manages users at boot time. Including:
@@ -46,6 +48,12 @@
private final Context mContext;
private final CarUserManagerHelper mCarUserManagerHelper;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private boolean mUser0Unlocked;
+ @GuardedBy("mLock")
+ private final ArrayList<Runnable> mUser0UnlockTasks = new ArrayList<>();
+
public CarUserService(
@Nullable Context context, @Nullable CarUserManagerHelper carUserManagerHelper) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -60,11 +68,13 @@
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "init");
}
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
+ if (mCarUserManagerHelper.isHeadlessSystemUser()) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(this, filter);
+ mContext.registerReceiver(this, filter);
+ }
}
@Override
@@ -79,6 +89,11 @@
public void dump(PrintWriter writer) {
writer.println(TAG);
writer.println("Context: " + mContext);
+ boolean user0Unlocked;
+ synchronized (mLock) {
+ user0Unlocked = mUser0Unlocked;
+ }
+ writer.println("User0Unlocked: " + user0Unlocked);
}
@Override
@@ -100,7 +115,7 @@
} else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
// Update last active user if the switched-to user is a persistent, non-system user.
- int currentUser = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ final int currentUser = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (currentUser > UserHandle.USER_SYSTEM
&& mCarUserManagerHelper.isPersistentUser(currentUser)) {
mCarUserManagerHelper.setLastActiveUser(currentUser);
@@ -108,6 +123,51 @@
}
}
+ /**
+ * Set user lock / unlocking status. This is coming from system server through ICar binder call.
+ * @param userHandle Handle of user
+ * @param unlocked unlocked (=true) or locked (=false)
+ */
+ public void setUserLockStatus(int userHandle, boolean unlocked) {
+ if (userHandle != UserHandle.USER_SYSTEM) {
+ return;
+ }
+ ArrayList<Runnable> tasks = null;
+ synchronized (mLock) {
+ // Assumes that car service need to do it only once during boot-up
+ if (unlocked && !mUser0Unlocked) {
+ tasks = new ArrayList<>(mUser0UnlockTasks);
+ mUser0UnlockTasks.clear();
+ mUser0Unlocked = unlocked;
+ }
+ }
+ if (tasks != null && tasks.size() > 0) {
+ Log.d(TAG, "User0 unlocked, run queued tasks:" + tasks.size());
+ for (Runnable r : tasks) {
+ r.run();
+ }
+ }
+ }
+
+ /**
+ * Run give runnable when user 0 is unlocked. If user 0 is already unlocked, it is
+ * run inside this call.
+ * @param r Runnable to run.
+ */
+ public void runOnUser0Unlock(Runnable r) {
+ boolean runNow = false;
+ synchronized (mLock) {
+ if (mUser0Unlocked) {
+ runNow = true;
+ } else {
+ mUser0UnlockTasks.add(r);
+ }
+ }
+ if (runNow) {
+ r.run();
+ }
+ }
+
private void setSystemUserRestrictions() {
// Disable adding accounts for system user.
mCarUserManagerHelper.setUserRestriction(mCarUserManagerHelper.getSystemUserInfo(),