diff options
| author | 2020-03-31 00:36:51 +0200 | |
|---|---|---|
| committer | 2020-04-24 16:12:27 +0200 | |
| commit | 9e1181302a4328b24965b90211eee49c4af6bb1d (patch) | |
| tree | dce7ad79165d42ad8321c1edb047287d2a7dcabb | |
| parent | 4a2288bf0e9fd87c815f7fc5cb69ecb882602513 (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.java | 39 | ||||
| -rw-r--r-- | core/java/android/os/ServiceManagerNative.java | 2 | ||||
| -rw-r--r-- | core/jni/Android.bp | 1 | ||||
| -rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
| -rw-r--r-- | core/jni/android_os_ServiceManager.cpp | 71 | ||||
| -rw-r--r-- | services/core/java/com/android/server/lights/LightsService.java | 50 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java | 13 |
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); |