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

Commit d42d9769 authored by Lingyu Feng's avatar Lingyu Feng
Browse files

Add "Include default display in topology" switch for projected mode

If the device is not in projected mode or if the switch is enabled, the
default display is always included in the topology. Otherwise:

- when an external display is added, the default display will be removed
  from the topology;
- when all the external displays are removed, the default display will
  be added back to the topology;
- when the user enables/disables the switch, the default display will be
  added to / removed from the topology.

This switch is only available for the device in projected mode, and when
the "Mirror built-in display" switch is disabled.

Bug: 407512389
Test: adb shell settings put secure include_default_display_in_topology
{1|0}
Test: atest DisplayManagerServiceTest
Flag: com.android.server.display.feature.flags.enable_default_display_in_topology_switch

Change-Id: I3ee34e3330e4c99a4e6cee2ddd45631b47d2d4b7
parent 1c41f230
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -158,6 +158,20 @@ public final class DisplayTopology implements Parcelable {
        return mPrimaryDisplayId;
    }

    /**
     * @hide
     */
    public boolean isEmpty() {
        return mRoot == null;
    }

    /**
     * @hide
     */
    public boolean hasMultipleDisplays() {
        return mRoot != null && mRoot.mChildren != null && !mRoot.mChildren.isEmpty();
    }

    /**
     * Add a display to the topology.
     * If this is the second display in the topology, it will be placed above the first display.
+71 −2
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.ROOT_UID;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Secure.INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY;
import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
import static android.provider.Settings.Secure.RESOLUTION_MODE_FULL;
import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
@@ -194,6 +195,7 @@ import com.android.server.display.utils.DebugUtils;
import com.android.server.display.utils.SensorUtils;
import com.android.server.input.InputManagerInternal;
import com.android.server.utils.FoldSettingProvider;
import com.android.server.wm.DesktopModeHelper;
import com.android.server.wm.SurfaceAnimationThread;
import com.android.server.wm.WindowManagerInternal;

@@ -549,6 +551,10 @@ public final class DisplayManagerService extends SystemService {

    private boolean mMirrorBuiltInDisplay;

    // Whether default display should be included in the display topology. Note that this should
    // only be used for the devices in projected mode.
    private boolean mIncludeDefaultDisplayInTopology;

    private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -690,8 +696,9 @@ public final class DisplayManagerService extends SystemService {
                        deliverTopologyUpdate(update.first);
                    };
            mDisplayTopologyCoordinator = new DisplayTopologyCoordinator(
                    this::isExtendedDisplayAllowed, topologyChangedCallback,
                    new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged);
                    this::isExtendedDisplayAllowed, this::shouldIncludeDefaultDisplayInTopology,
                    topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot,
                    backupManager::dataChanged, mFlags);
        } else {
            mDisplayTopologyCoordinator = null;
        }
@@ -874,6 +881,12 @@ public final class DisplayManagerService extends SystemService {
            if (mFlags.isDisplayContentModeManagementEnabled()) {
                updateMirrorBuiltInDisplaySettingLocked(/*shouldSendDisplayChangeEvent=*/ false);
            }

            if (mFlags.isDefaultDisplayInTopologySwitchEnabled()) {
                mIncludeDefaultDisplayInTopology = mInjector.canInternalDisplayHostDesktops(
                        mContext) || (Settings.Secure.getIntForUser(mContext.getContentResolver(),
                        INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY, 0, UserHandle.USER_CURRENT) != 0);
            }
        }

        mDisplayModeDirector.setDesiredDisplayModeSpecsListener(
@@ -1230,6 +1243,14 @@ public final class DisplayManagerService extends SystemService {
                        Settings.Secure.getUriFor(
                                MIRROR_BUILT_IN_DISPLAY), false, this, UserHandle.USER_ALL);
            }

            if (mFlags.isDefaultDisplayInTopologySwitchEnabled()
                    && !mInjector.canInternalDisplayHostDesktops(mContext)) {
                mContext.getContentResolver().registerContentObserver(
                        Settings.Secure.getUriFor(
                                Settings.Secure.INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY),
                        false, this, UserHandle.USER_ALL);
            }
        }

        @Override
@@ -1252,6 +1273,15 @@ public final class DisplayManagerService extends SystemService {
                }
                return;
            }
            if (Settings.Secure.getUriFor(INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY).equals(uri)) {
                synchronized (mSyncRoot) {
                    if (mFlags.isDefaultDisplayInTopologySwitchEnabled()
                            && !mInjector.canInternalDisplayHostDesktops(mContext)) {
                        handleIncludeDefaultDisplayInTopologySettingChangeLocked();
                    }
                }
                return;
            }
        }
    }

@@ -1287,6 +1317,36 @@ public final class DisplayManagerService extends SystemService {
        return true;
    }

    private void handleIncludeDefaultDisplayInTopologySettingChangeLocked() {
        ContentResolver resolver = mContext.getContentResolver();
        final boolean includeDefaultDisplayInTopology = Settings.Secure.getIntForUser(resolver,
                INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY, 0, UserHandle.USER_CURRENT) != 0;

        if (mIncludeDefaultDisplayInTopology == includeDefaultDisplayInTopology) {
            return;
        }
        mIncludeDefaultDisplayInTopology = includeDefaultDisplayInTopology;

        // This switch is not available when the "Mirror built-in display" switch is enabled.
        if (shouldMirrorBuiltInDisplay()) {
            return;
        }

        if (mDisplayTopologyCoordinator != null) {
            if (mIncludeDefaultDisplayInTopology) {
                final DisplayInfo info = mLogicalDisplayMapper.getDisplayLocked(
                        Display.DEFAULT_DISPLAY).getDisplayInfoLocked();
                mDisplayTopologyCoordinator.onDisplayAdded(info);
            } else {
                // The default display can only be removed when there are multiple displays in the
                // topology to ensure the topology is not empty.
                if (mDisplayTopologyCoordinator.getTopology().hasMultipleDisplays()) {
                    mDisplayTopologyCoordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY);
                }
            }
        }
    }

    private void restoreResolutionFromBackup() {
        int savedMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.SCREEN_RESOLUTION_MODE,
@@ -2441,6 +2501,11 @@ public final class DisplayManagerService extends SystemService {
        }
    }

    boolean shouldIncludeDefaultDisplayInTopology() {
        return mInjector.canInternalDisplayHostDesktops(mContext)
                || mIncludeDefaultDisplayInTopology;
    }

    @SuppressLint("AndroidFrameworkRequiresPermission")
    private void handleLogicalDisplayAddedLocked(LogicalDisplay display) {
        final int displayId = display.getDisplayIdLocked();
@@ -3992,6 +4057,10 @@ public final class DisplayManagerService extends SystemService {
                    blanker, logicalDisplay, brightnessTracker, brightnessSetting,
                    onBrightnessChangeRunnable, hbmMetadata, bootCompleted, flags);
        }

        boolean canInternalDisplayHostDesktops(Context context) {
            return DesktopModeHelper.canInternalDisplayHostDesktops(context);
        }
    }

    @VisibleForTesting
+56 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.display;

import android.annotation.Nullable;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayTopology;
import android.hardware.display.DisplayTopologyGraph;
import android.util.Pair;
@@ -26,6 +28,8 @@ import android.view.DisplayInfo;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.display.feature.DisplayManagerFlags;

import java.io.PrintWriter;
import java.util.HashMap;
@@ -48,6 +52,8 @@ class DisplayTopologyCoordinator {
        return info.uniqueId;
    }

    private final Injector mInjector;

    // Persistent data store for display topologies.
    private final DisplayTopologyStore mTopologyStore;

@@ -65,6 +71,13 @@ class DisplayTopologyCoordinator {
     */
    private final BooleanSupplier mIsExtendedDisplayAllowed;

    /**
     * Check if the default display should be included in the topology when there are other displays
     * present. If not, remove the default when another display is added, and add the default
     * display back to the topology when all other displays are removed.
     */
    private final BooleanSupplier mShouldIncludeDefaultDisplayInTopology;

    /**
     * Callback used to send topology updates.
     * Should be invoked from the corresponding executor.
@@ -74,28 +87,35 @@ class DisplayTopologyCoordinator {
    private final Executor mTopologyChangeExecutor;
    private final DisplayManagerService.SyncRoot mSyncRoot;
    private final Runnable mTopologySavedCallback;
    private final DisplayManagerFlags mFlags;

    DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayAllowed,
            BooleanSupplier shouldIncludeDefaultDisplayInTopology,
            Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback,
            Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
            Runnable topologySavedCallback) {
        this(new Injector(), isExtendedDisplayAllowed, onTopologyChangedCallback,
                topologyChangeExecutor, syncRoot, topologySavedCallback);
            Runnable topologySavedCallback, DisplayManagerFlags flags) {
        this(new Injector(), isExtendedDisplayAllowed, shouldIncludeDefaultDisplayInTopology,
                onTopologyChangedCallback, topologyChangeExecutor, syncRoot, topologySavedCallback,
                flags);
    }

    @VisibleForTesting
    DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayAllowed,
            BooleanSupplier shouldIncludeDefaultDisplayInTopology,
            Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback,
            Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
            Runnable topologySavedCallback) {
            Runnable topologySavedCallback, DisplayManagerFlags flags) {
        mInjector = injector;
        mTopology = injector.getTopology();
        mIsExtendedDisplayAllowed = isExtendedDisplayAllowed;
        mShouldIncludeDefaultDisplayInTopology = shouldIncludeDefaultDisplayInTopology;
        mOnTopologyChangedCallback = onTopologyChangedCallback;
        mTopologyChangeExecutor = topologyChangeExecutor;
        mSyncRoot = syncRoot;
        mTopologyStore = injector.createTopologyStore(
                mDisplayIdToUniqueIdMapping, mUniqueIdToDisplayIdMapping);
        mTopologySavedCallback = topologySavedCallback;
        mFlags = flags;
    }

    /**
@@ -114,6 +134,16 @@ class DisplayTopologyCoordinator {
            restoreTopologyLocked();
            sendTopologyUpdateLocked();
        }

        if (mFlags.isDefaultDisplayInTopologySwitchEnabled()) {
            // If the default display should not be included in the topology, then when a
            // non-default display is added, remove the default display from the topology.
            if (info.displayId != Display.DEFAULT_DISPLAY
                    && !mShouldIncludeDefaultDisplayInTopology.getAsBoolean()
                    && mTopology.hasMultipleDisplays()) {
                onDisplayRemoved(Display.DEFAULT_DISPLAY);
            }
        }
    }

    /**
@@ -145,6 +175,16 @@ class DisplayTopologyCoordinator {
                sendTopologyUpdateLocked();
            }
        }

        // If the default display should not be included in the topology, then when all non-default
        // displays are removed, add the default display back to the topology.
        if (mFlags.isDefaultDisplayInTopologySwitchEnabled()) {
            if (displayId != Display.DEFAULT_DISPLAY
                    && !mShouldIncludeDefaultDisplayInTopology.getAsBoolean()
                    && mTopology.isEmpty()) {
                onDisplayAdded(mInjector.getDisplayInfo(Display.DEFAULT_DISPLAY));
            }
        }
    }

    /**
@@ -225,6 +265,9 @@ class DisplayTopologyCoordinator {
    }

    private boolean isDisplayAllowedInTopology(DisplayInfo info, boolean shouldLog) {
        if (info == null) {
            return false;
        }
        if (info.type != Display.TYPE_INTERNAL && info.type != Display.TYPE_EXTERNAL
                && info.type != Display.TYPE_OVERLAY) {
            if (shouldLog) {
@@ -275,6 +318,9 @@ class DisplayTopologyCoordinator {

    @VisibleForTesting
    static class Injector {
        private final DisplayManagerInternal mDisplayManagerInternal
                = LocalServices.getService(DisplayManagerInternal.class);

        DisplayTopology getTopology() {
            return new DisplayTopology();
        }
@@ -294,5 +340,11 @@ class DisplayTopologyCoordinator {
                }
            });
        }

        @Nullable
        DisplayInfo getDisplayInfo(int displayId) {
            return mDisplayManagerInternal == null ? null
                    : mDisplayManagerInternal.getDisplayInfo(displayId);
        }
    }
}
+139 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUST
import static android.hardware.display.DisplayManagerGlobal.INTERNAL_EVENT_FLAG_TOPOLOGY_UPDATED;
import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_SYSTEM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Secure.INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY;
import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
@@ -64,6 +65,7 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@@ -197,6 +199,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;

import java.lang.reflect.Field;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
@@ -380,6 +383,11 @@ public class DisplayManagerServiceTest {
        boolean getHdrOutputConversionSupport() {
            return true;
        }

        @Override
        boolean canInternalDisplayHostDesktops(Context context) {
            return false;
        }
    }

    private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
@@ -406,6 +414,7 @@ public class DisplayManagerServiceTest {
    @Mock DisplayManagerInternal mMockDisplayManagerInternal;
    @Mock ActivityManagerInternal mMockActivityManagerInternal;
    @Mock DisplayAdapter mMockDisplayAdapter;
    @Mock DisplayTopologyCoordinator mMockDisplayTopologyCoordinator;

    @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
    @Mock DisplayManagerFlags mMockFlags;
@@ -4343,6 +4352,126 @@ public class DisplayManagerServiceTest {
                .getDefaultDozeBrightness(Display.DEFAULT_DISPLAY));
    }

    @Test
    public void testIncludeDefaultDisplayInTopologySwitch_flagDisabled() {
        when(mMockFlags.isDefaultDisplayInTopologySwitchEnabled()).thenReturn(false);
        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                0);

        DisplayManagerService displayManager = new DisplayManagerService(mContext,
                mBasicInjector);
        setFieldValue(displayManager, "mDisplayTopologyCoordinator",
                mMockDisplayTopologyCoordinator);
        displayManager.systemReady(/* safeMode= */ false);
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isFalse();

        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                1);
        final ContentObserver observer = displayManager.getSettingsObserver();
        observer.onChange(false, Settings.Secure.getUriFor(INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY));
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isFalse();
        verify(mMockDisplayTopologyCoordinator, never()).onDisplayAdded(any());
    }

    @Test
    public void testIncludeDefaultDisplayInTopologySwitch_internalDisplayCanHostDesktops() {
        when(mMockFlags.isDefaultDisplayInTopologySwitchEnabled()).thenReturn(true);
        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                0);

        DisplayManagerService displayManager = new DisplayManagerService(
                mContext, new BasicInjector() {
            @Override
            boolean canInternalDisplayHostDesktops(Context context) {
                return true;
            }
        });

        displayManager.systemReady(/* safeMode= */ false);
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isTrue();

        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                1);
        final ContentObserver observer = displayManager.getSettingsObserver();
        observer.onChange(false, Settings.Secure.getUriFor(INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY));
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isTrue();
        verify(mMockDisplayTopologyCoordinator, never()).onDisplayAdded(any());
    }

    @Test
    public void testIncludeDefaultDisplayInTopologySwitch_addDefaultDisplayWhenEnableSwitch() {
        when(mMockFlags.isDefaultDisplayInTopologySwitchEnabled()).thenReturn(true);
        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                0);

        DisplayManagerService displayManager = new DisplayManagerService(mContext,
                mBasicInjector);
        setFieldValue(displayManager, "mDisplayTopologyCoordinator",
                mMockDisplayTopologyCoordinator);
        displayManager.systemReady(/* safeMode= */ false);
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isFalse();

        createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
        DisplayInfo defaultDisplayInfo = displayManager.getLogicalDisplayMapper().
                getDisplayLocked(Display.DEFAULT_DISPLAY).getDisplayInfoLocked();
        clearInvocations(mMockDisplayTopologyCoordinator);

        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                1);
        final ContentObserver observer = displayManager.getSettingsObserver();
        observer.onChange(false, Settings.Secure.getUriFor(INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY));
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isTrue();
        verify(mMockDisplayTopologyCoordinator).onDisplayAdded(defaultDisplayInfo);
    }

    @Test
    public void testIncludeDefaultDisplayInTopologySwitch_removeDefaultDisplayWhenDisableSwitch() {
        when(mMockFlags.isDefaultDisplayInTopologySwitchEnabled()).thenReturn(true);
        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 0);
        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                1);

        DisplayManagerService displayManager = new DisplayManagerService(mContext,
                mBasicInjector);
        setFieldValue(displayManager, "mDisplayTopologyCoordinator",
                mMockDisplayTopologyCoordinator);
        displayManager.systemReady(/* safeMode= */ false);
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isTrue();

        DisplayTopology mockTopology = mock(DisplayTopology.class);
        doReturn(true).when(mockTopology).hasMultipleDisplays();
        doReturn(mockTopology).when(mMockDisplayTopologyCoordinator).getTopology();

        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                0);
        final ContentObserver observer = displayManager.getSettingsObserver();
        observer.onChange(false, Settings.Secure.getUriFor(INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY));
        assertThat(displayManager.shouldIncludeDefaultDisplayInTopology()).isFalse();
        verify(mMockDisplayTopologyCoordinator).onDisplayRemoved(Display.DEFAULT_DISPLAY);
    }

    @Test
    public void testIncludeDefaultDisplayInTopologySwitch_mirrorBuiltInDisplay() {
        when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
        when(mMockFlags.isDefaultDisplayInTopologySwitchEnabled()).thenReturn(true);
        Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                0);

        DisplayManagerService displayManager = new DisplayManagerService(mContext,
                mBasicInjector);
        setFieldValue(displayManager, "mDisplayTopologyCoordinator",
                mMockDisplayTopologyCoordinator);
        displayManager.systemReady(/* safeMode= */ false);

        Settings.Secure.putInt(mContext.getContentResolver(), INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY,
                1);
        final ContentObserver observer = displayManager.getSettingsObserver();
        observer.onChange(false, Settings.Secure.getUriFor(INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY));
        verify(mMockDisplayTopologyCoordinator, never()).onDisplayAdded(any());
    }

    private void initDisplayPowerController(DisplayManagerInternal localService) {
        localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
            @Override
@@ -4748,6 +4877,16 @@ public class DisplayManagerServiceTest {
        return topology;
    }

    private void setFieldValue(Object o, String fieldName, Object value) {
        try {
            final Field field = o.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(o, value);
        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    private static class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub
            implements DisplayManagerInternal.DisplayGroupListener {
        int mDisplayId;
+81 −2

File changed.

Preview size limit exceeded, changes collapsed.