Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad244fda authored by petsjonkin's avatar petsjonkin
Browse files

DisplayManager : adding plugins support

Bug: b/354063547
Test: atest com.android.server.display.plugin
Flag: com.android.server.display.feature.flags.enable_plugin_manager
Change-Id: Ib00fc05c2a46563818a82938bf07187556216342
parent 286cb5e7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -29,5 +29,7 @@

    <!-- Whether even dimmer feature is enabled. -->
    <bool name="config_evenDimmerEnabled">false</bool>
    <!-- Jar file path to look for PluginProvider -->
    <string name="config_pluginsProviderJarPath"/>

</resources>
+1 −0
Original line number Diff line number Diff line
@@ -5582,6 +5582,7 @@

  <!-- DisplayManager configs. -->
  <java-symbol type="bool" name="config_evenDimmerEnabled" />
  <java-symbol type="string" name="config_pluginsProviderJarPath" />

  <java-symbol type="bool" name="config_watchlistUseFileHashesCache" />
  <java-symbol type="string" name="config_defaultContextualSearchPackageName" />
+9 −3
Original line number Diff line number Diff line
@@ -177,6 +177,7 @@ import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.display.plugin.PluginManager;
import com.android.server.display.utils.DebugUtils;
import com.android.server.display.utils.SensorUtils;
import com.android.server.input.InputManagerInternal;
@@ -583,6 +584,7 @@ public final class DisplayManagerService extends SystemService {

    private final DisplayNotificationManager mDisplayNotificationManager;
    private final ExternalDisplayStatsService mExternalDisplayStatsService;
    private final PluginManager mPluginManager;

    // Manages the relative placement of extended displays
    @Nullable
@@ -669,6 +671,7 @@ public final class DisplayManagerService extends SystemService {
        } else {
            mDisplayTopologyCoordinator = null;
        }
        mPluginManager = new PluginManager(mContext, mFlags);
    }

    public void setupSchedulerPolicies() {
@@ -739,6 +742,7 @@ public final class DisplayManagerService extends SystemService {
            mLogicalDisplayMapper.onBootCompleted();
            mDisplayNotificationManager.onBootCompleted();
            mExternalDisplayPolicy.onBootCompleted();
            mPluginManager.onBootCompleted();
        }
    }

@@ -3543,6 +3547,9 @@ public final class DisplayManagerService extends SystemService {
        SparseArray<DisplayPowerController> displayPowerControllersLocal = new SparseArray<>();
        int displayPowerControllerCount;

        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
        ipw.increaseIndent();

        synchronized (mSyncRoot) {
            brightnessTrackerLocal = mBrightnessTracker;

@@ -3590,9 +3597,6 @@ public final class DisplayManagerService extends SystemService {
                pw.println("  Display SdrBrightness=" + brightnessPair.sdrBrightness);
            }

            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
            ipw.increaseIndent();

            pw.println();
            pw.println("Display Adapters: size=" + mDisplayAdapters.size());
            pw.println("------------------------");
@@ -3655,6 +3659,8 @@ public final class DisplayManagerService extends SystemService {
            pw.println();
            mDisplayTopologyCoordinator.dump(pw);
        }
        pw.println();
        mPluginManager.dump(ipw);

        pw.println();
        mFlags.dump(pw);
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.server.display.plugin;

import com.android.tools.r8.keepanno.annotations.KeepForApi;

import java.io.PrintWriter;

/**
 * Interface that OEMs should implement in order to inject custom code to system process.
 * Communication between OEM Plugin and Framework is implemented via {@link PluginStorage}.
 * OEM Plugin pushes values to PluginStorage, that are picked up by
 * {@link PluginManager.PluginChangeListener}, implemented on Framework side.
 * Avoid calling heavy operations in constructor - it will be called during boot and will
 * negatively impact boot time. Use onBootComplete and separate thread for long running operations.
 */
@KeepForApi
public interface Plugin {

    /**
     * Called when device boot completed
     */
    void onBootCompleted();

    /**
     * Print the object's state and debug information into the given stream.
     */
    void dump(PrintWriter pw);
}
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.server.display.plugin;

import android.annotation.Nullable;
import android.content.Context;
import android.text.TextUtils;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SystemServerClassLoaderFactory;
import com.android.server.display.feature.DisplayManagerFlags;

import dalvik.system.PathClassLoader;

import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;

/**
 * Responsible for loading Plugins. Plugins and PluginSupplier are loaded from
 * standalone system jar.
 * Plugin manager will look for PROVIDER_IMPL_CLASS in configured jar.
 * After device booted, PluginManager will delegate this call to each Plugin
 */
public class PluginManager {
    private static final String PROVIDER_IMPL_CLASS =
            "com.android.server.display.plugin.PluginsProviderImpl";
    private static final String TAG = "PluginManager";

    private final DisplayManagerFlags mFlags;
    private final PluginStorage mPluginStorage;
    private final List<Plugin> mPlugins;

    public PluginManager(Context context, DisplayManagerFlags flags) {
        this(context, flags, new Injector());
    }

    @VisibleForTesting
    PluginManager(Context context, DisplayManagerFlags flags, Injector injector) {
        mFlags = flags;
        mPluginStorage = injector.getPluginStorage();
        if (mFlags.isPluginManagerEnabled()) {
            mPlugins = Collections.unmodifiableList(injector.loadPlugins(context, mPluginStorage));
            Slog.d(TAG, "loaded Plugins:" + mPlugins);
        } else {
            mPlugins = List.of();
            Slog.d(TAG, "PluginManager disabled");
        }
    }

    /**
     * Forwards boot completed event to Plugins
     */
    public void onBootCompleted() {
        mPlugins.forEach(Plugin::onBootCompleted);
    }

    /**
     * Adds change listener for particular plugin type
     */
    public <T> void subscribe(PluginType<T> type, PluginChangeListener<T> listener) {
        mPluginStorage.addListener(type, listener);
    }

    /**
     * Removes change listener
     */
    public <T> void unsubscribe(PluginType<T> type, PluginChangeListener<T> listener) {
        mPluginStorage.removeListener(type, listener);
    }

    /**
     * Print the object's state and debug information into the given stream.
     */
    public void dump(PrintWriter pw) {
        pw.println("PluginManager:");
        mPluginStorage.dump(pw);
        for (Plugin plugin : mPlugins) {
            plugin.dump(pw);
        }
    }

    /**
     * Listens for changes in PluginStorage for a particular type
     * @param <T> plugin value type
     */
    public interface PluginChangeListener<T> {
        /**
         * Called when Plugin value changed
         */
        void onChanged(@Nullable T value);
    }

    static class Injector {
        PluginStorage getPluginStorage() {
            return new PluginStorage();
        }

        List<Plugin> loadPlugins(Context context, PluginStorage storage) {
            String providerJarPath = context
                    .getString(com.android.internal.R.string.config_pluginsProviderJarPath);
            Slog.d(TAG, "loading plugins from:" + providerJarPath);
            if (TextUtils.isEmpty(providerJarPath)) {
                return List.of();
            }
            try {
                PathClassLoader pathClassLoader =
                        SystemServerClassLoaderFactory.getOrCreateClassLoader(
                                providerJarPath, getClass().getClassLoader(), false);
                @SuppressWarnings("PrivateApi")
                Class<? extends PluginsProvider> cp = pathClassLoader.loadClass(PROVIDER_IMPL_CLASS)
                        .asSubclass(PluginsProvider.class);
                PluginsProvider provider = cp.getDeclaredConstructor().newInstance();
                return provider.getPlugins(context, storage);
            } catch (ClassNotFoundException e) {
                Slog.e(TAG, "loading failed: " + PROVIDER_IMPL_CLASS + " is not found in"
                        + providerJarPath, e);
            } catch (InvocationTargetException | InstantiationException | IllegalAccessException
                     | NoSuchMethodException e) {
                Slog.e(TAG, "Class instantiation failed", e);
            }
            return List.of();
        }
    }
}
Loading