summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Robin Lee <rgl@google.com> 2020-03-31 00:36:51 +0200
committer Robin Lee <rgl@google.com> 2020-04-24 16:12:27 +0200
commit9e1181302a4328b24965b90211eee49c4af6bb1d (patch)
treedce7ad79165d42ad8321c1edb047287d2a7dcabb
parent4a2288bf0e9fd87c815f7fc5cb69ecb882602513 (diff)
Use waitForDeclaredService to get Lights HAL
We cache the reference to the HAL and use a DeathRecipient to keep track of whether it's still good. Adds waitForService and waitForDeclaredService to the ServiceManager Java implementation because they were missing. Test: atest LightsManagerTest Test: atest LightsServiceTest Bug: 152509747 Bug: 154631113 Bug: 154627432 Bug: 154629168 Change-Id: Ife8471f3a7e47b48bb31015ddf40f1d7fef6240f
-rw-r--r--core/java/android/os/ServiceManager.java39
-rw-r--r--core/java/android/os/ServiceManagerNative.java2
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_os_ServiceManager.cpp71
-rw-r--r--services/core/java/com/android/server/lights/LightsService.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java13
7 files changed, 159 insertions, 19 deletions
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 74ff310ad3af..b654707a683b 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Log;
@@ -219,6 +220,44 @@ public final class ServiceManager {
}
/**
+ * Returns whether the specified service is declared.
+ *
+ * @return true if the service is declared somewhere (eg. VINTF manifest) and
+ * waitForService should always be able to return the service.
+ */
+ public static boolean isDeclared(@NonNull String name) {
+ try {
+ return getIServiceManager().isDeclared(name);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in isDeclared", e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the specified service from the service manager.
+ *
+ * If the service is not running, servicemanager will attempt to start it, and this function
+ * will wait for it to be ready.
+ *
+ * @return {@code null} only if there are permission problems or fatal errors.
+ */
+ public static native IBinder waitForService(@NonNull String name);
+
+ /**
+ * Returns the specified service from the service manager, if declared.
+ *
+ * If the service is not running, servicemanager will attempt to start it, and this function
+ * will wait for it to be ready.
+ *
+ * @return {@code null} if the service is not declared in the manifest, or if there are
+ * permission problems, or if there are fatal errors.
+ */
+ public static IBinder waitForDeclaredService(@NonNull String name) {
+ return isDeclared(name) ? waitForService(name) : null;
+ }
+
+ /**
* Return a list of all currently running services.
* @return an array of all currently running services, or <code>null</code> in
* case of an exception
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 39ddcb2af3c2..91b56fbbc38e 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -87,7 +87,7 @@ class ServiceManagerProxy implements IServiceManager {
}
public boolean isDeclared(String name) throws RemoteException {
- throw new RemoteException();
+ return mServiceManager.isDeclared(name);
}
public void registerClientCallback(String name, IBinder service, IClientCallback cb)
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 189a0a72e42c..bd7bc4cf4ceb 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -123,6 +123,7 @@ cc_library_shared {
"android_os_MessageQueue.cpp",
"android_os_Parcel.cpp",
"android_os_SELinux.cpp",
+ "android_os_ServiceManager.cpp",
"android_os_SharedMemory.cpp",
"android_os_storage_StorageManager.cpp",
"android_os_UEventObserver.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 4cb2e975a58b..6fcaddf34322 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -136,6 +136,7 @@ extern int register_android_os_HwBlob(JNIEnv *env);
extern int register_android_os_HwParcel(JNIEnv *env);
extern int register_android_os_HwRemoteBinder(JNIEnv *env);
extern int register_android_os_NativeHandle(JNIEnv *env);
+extern int register_android_os_ServiceManager(JNIEnv *env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_SELinux(JNIEnv* env);
@@ -1461,6 +1462,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_HwParcel),
REG_JNI(register_android_os_HwRemoteBinder),
REG_JNI(register_android_os_NativeHandle),
+ REG_JNI(register_android_os_ServiceManager),
REG_JNI(register_android_os_storage_StorageManager),
REG_JNI(register_android_os_VintfObject),
REG_JNI(register_android_os_VintfRuntimeInfo),
diff --git a/core/jni/android_os_ServiceManager.cpp b/core/jni/android_os_ServiceManager.cpp
new file mode 100644
index 000000000000..c7479492d404
--- /dev/null
+++ b/core/jni/android_os_ServiceManager.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "ServiceManager"
+//#define LOG_NDEBUG 0
+#include <android-base/logging.h>
+
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
+
+namespace android {
+
+// Native because we have a client-side wait in waitForService() and we do not
+// want an unnecessary second copy of it.
+static jobject android_os_ServiceManager_waitForService(
+ JNIEnv *env,
+ jclass /* clazzObj */,
+ jstring serviceNameObj) {
+
+ const jchar* serviceName = env->GetStringCritical(serviceNameObj, nullptr);
+ if (!serviceName) {
+ jniThrowNullPointerException(env, nullptr);
+ return nullptr;
+ }
+ String16 nameCopy = String16(reinterpret_cast<const char16_t *>(serviceName),
+ env->GetStringLength(serviceNameObj));
+ env->ReleaseStringCritical(serviceNameObj, serviceName);
+
+ sp<IBinder> service = defaultServiceManager()->waitForService(nameCopy);
+
+ if (!service) {
+ return nullptr;
+ }
+
+ return javaObjectForIBinder(env, service);
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod method_table[] = {
+ /* name, signature, funcPtr */
+ {
+ "waitForService",
+ "(Ljava/lang/String;)Landroid/os/IBinder;",
+ (void*)android_os_ServiceManager_waitForService
+ },
+};
+
+int register_android_os_ServiceManager(JNIEnv* env) {
+ return RegisterMethodsOrDie(
+ env, "android/os/ServiceManager", method_table, NELEM(method_table));
+}
+
+}; // namespace android
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 3c6e8d29cae0..8888108100e1 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -25,6 +25,7 @@ import android.hardware.light.ILights;
import android.hardware.lights.ILightsManager;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -37,16 +38,17 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
+import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.internal.BrightnessSynchronizer;
import com.android.server.SystemService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Supplier;
public class LightsService extends SystemService {
static final String TAG = "LightsService";
@@ -55,7 +57,8 @@ public class LightsService extends SystemService {
private final LightImpl[] mLightsByType = new LightImpl[LightsManager.LIGHT_ID_COUNT];
private final SparseArray<LightImpl> mLightsById = new SparseArray<>();
- private ILights mVintfLights = null;
+ @Nullable
+ private final Supplier<ILights> mVintfLights;
@VisibleForTesting
final LightsManagerBinderService mManagerService;
@@ -391,7 +394,7 @@ public class LightsService extends SystemService {
lightState.flashOnMs = onMS;
lightState.flashOffMs = offMS;
lightState.brightnessMode = (byte) brightnessMode;
- mVintfLights.setLightState(mHwLight.id, lightState);
+ mVintfLights.get().setLightState(mHwLight.id, lightState);
} else {
setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode);
}
@@ -435,19 +438,17 @@ public class LightsService extends SystemService {
}
public LightsService(Context context) {
- this(context,
- ILights.Stub.asInterface(
- ServiceManager.getService("android.hardware.light.ILights/default")),
- Looper.myLooper());
+ this(context, new VintfHalCache(), Looper.myLooper());
}
@VisibleForTesting
- LightsService(Context context, ILights service, Looper looper) {
+ LightsService(Context context, Supplier<ILights> service, Looper looper) {
super(context);
mH = new Handler(looper);
- mVintfLights = service;
- mManagerService = new LightsManagerBinderService();
+ mVintfLights = service.get() != null ? service : null;
+
populateAvailableLights(context);
+ mManagerService = new LightsManagerBinderService();
}
private void populateAvailableLights(Context context) {
@@ -467,7 +468,7 @@ public class LightsService extends SystemService {
private void populateAvailableLightsFromAidl(Context context) {
try {
- for (HwLight hwLight : mVintfLights.getLights()) {
+ for (HwLight hwLight : mVintfLights.get().getLights()) {
mLightsById.put(hwLight.id, new LightImpl(context, hwLight));
}
} catch (RemoteException ex) {
@@ -514,6 +515,33 @@ public class LightsService extends SystemService {
}
};
+ private static class VintfHalCache implements Supplier<ILights>, IBinder.DeathRecipient {
+ @GuardedBy("this")
+ private ILights mInstance = null;
+
+ @Override
+ public synchronized ILights get() {
+ if (mInstance == null) {
+ IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
+ "android.hardware.light.ILights/default"));
+ if (binder != null) {
+ mInstance = ILights.Stub.asInterface(binder);
+ try {
+ binder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register DeathRecipient for " + mInstance);
+ }
+ }
+ }
+ return mInstance;
+ }
+
+ @Override
+ public synchronized void binderDied() {
+ mInstance = null;
+ }
+ }
+
static native void setLight_native(int light, int color, int mode,
int onMS, int offMS, int brightnessMode);
}
diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
index aa923e22444d..d58937ea9cab 100644
--- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
@@ -16,12 +16,11 @@
package com.android.server.lights;
-import static android.hardware.lights.LightsRequest.Builder;
-
import static android.graphics.Color.BLACK;
import static android.graphics.Color.BLUE;
import static android.graphics.Color.GREEN;
import static android.graphics.Color.WHITE;
+import static android.hardware.lights.LightsRequest.Builder;
import static com.google.common.truth.Truth.assertThat;
@@ -93,7 +92,7 @@ public class LightsServiceTest {
@Test
public void testGetLights_filtersSystemLights() {
- LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
LightsManager manager = new LightsManager(mContext, service.mManagerService);
// When lights are listed, only the 4 MICROPHONE lights should be visible.
@@ -102,7 +101,7 @@ public class LightsServiceTest {
@Test
public void testControlMultipleLights() {
- LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
LightsManager manager = new LightsManager(mContext, service.mManagerService);
// When the session requests to turn 3/4 lights on:
@@ -124,7 +123,7 @@ public class LightsServiceTest {
@Test
public void testControlLights_onlyEffectiveForLifetimeOfClient() {
- LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
LightsManager manager = new LightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
@@ -145,7 +144,7 @@ public class LightsServiceTest {
@Test
public void testControlLights_firstCallerWinsContention() {
- LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
LightsManager manager = new LightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
@@ -171,7 +170,7 @@ public class LightsServiceTest {
@Test
public void testClearLight() {
- LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
LightsManager manager = new LightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);