diff --git a/Android.bp b/Android.bp index 0d41864e2c1d7..ab3aeecadcc61 100644 --- a/Android.bp +++ b/Android.bp @@ -142,6 +142,7 @@ filegroup { ":deviceproductinfoconstants_aidl", ":adbrootservice_aidl", + ":lmofreeform_aidl", // For the generated R.java and Manifest.java ":framework-res{.aapt.srcjar}", diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 2174513c83e56..b149d03f351ad 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -24,17 +24,21 @@ import android.hardware.SensorManager; import android.hardware.input.HostUsiVersion; import android.os.Handler; +import android.os.IBinder; import android.os.PowerManager; import android.util.IntArray; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; +import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.RefreshRateRange; import android.view.SurfaceControl.Transaction; import android.window.DisplayWindowPolicyController; import android.window.ScreenCaptureInternal; +import com.libremobileos.freeform.ILMOFreeformDisplayCallback; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; @@ -501,6 +505,17 @@ public abstract RefreshRateRange getRefreshRateForDisplayAndSensor( */ public abstract IntArray getDisplayIds(); + // LMOFreeform + public abstract void createFreeformLocked(String name, ILMOFreeformDisplayCallback callback, + int width, int height, int densityDpi, boolean secure, boolean ownContentOnly, + boolean shouldShowSystemDecorations, Surface surface, float refreshRate, + long presentationDeadlineNanos); + + public abstract void resizeFreeform(IBinder appToken, int width, int height, + int densityDpi); + + public abstract void releaseFreeform(IBinder appToken); + /** * Get group id for given display id */ diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index dbb68372ab985..ac3a59d5472a0 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -30,13 +30,17 @@ import android.content.res.Resources.Theme; import android.content.res.XmlResourceParser; import android.os.SystemClock; +import android.os.SystemProperties; import android.ravenwood.annotation.RavenwoodIgnore; import android.ravenwood.annotation.RavenwoodKeepPartialClass; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.TimeUtils; import android.util.Xml; import android.view.InflateException; +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -224,6 +228,20 @@ public static long getExpectedPresentationTimeMillis() { public static Animation loadAnimation(Context context, @AnimRes int id) throws NotFoundException { + if (SystemProperties.getBoolean("persist.sys.activity_anim_perf_override", false)) { + ActivityAnimations.maybeInit(context); + switch (id) { + case R.anim.activity_open_enter: + return ActivityAnimations.getOpenEnter(); + case R.anim.activity_open_exit: + return ActivityAnimations.getOpenExit(); + case R.anim.activity_close_enter: + return ActivityAnimations.getCloseEnter(); + case R.anim.activity_close_exit: + return ActivityAnimations.getCloseExit(); + } + } + XmlResourceParser parser = null; try { parser = context.getResources().getAnimation(id); @@ -504,4 +522,100 @@ private static Interpolator createInterpolatorFromXml( } return interpolator; } + + /** @hide */ + public final class ActivityAnimations { + + private static Animation sOpenEnter; + private static Animation sOpenExit; + private static Animation sCloseEnter; + private static Animation sCloseExit; + + private static Interpolator sFastOutExtraSlowInInterpolator; + + private static final float DISTANCE = 0.1f; + + private ActivityAnimations() {} + + /** @hide */ + public static void maybeInit(Context context) { + if (sFastOutExtraSlowInInterpolator == null) { + sFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator( + context, R.interpolator.fast_out_extra_slow_in); + } + } + + private static class ActivityAnimFactory { + private float fromX = 0f, toX = 0f; + private long duration = 200L; + + public ActivityAnimFactory fromX(float ratio) { + this.fromX = ratio; + return this; + } + + public ActivityAnimFactory toX(float ratio) { + this.toX = ratio; + return this; + } + + public Animation build() { + AnimationSet animationSet = new AnimationSet(false); + TranslateAnimation slide = new TranslateAnimation( + Animation.RELATIVE_TO_SELF, fromX, + Animation.RELATIVE_TO_SELF, toX, + Animation.RELATIVE_TO_SELF, 0f, + Animation.RELATIVE_TO_SELF, 0f + ); + slide.setDuration(duration); + slide.setInterpolator(sFastOutExtraSlowInInterpolator); + animationSet.addAnimation(slide); + return animationSet; + } + } + + /** @hide */ + public static Animation getOpenEnter() { + if (sOpenEnter == null) { + sOpenEnter = new ActivityAnimFactory() + .fromX(1.0f) + .toX(0.0f) + .build(); + } + return sOpenEnter; + } + + /** @hide */ + public static Animation getOpenExit() { + if (sOpenExit == null) { + sOpenExit = new ActivityAnimFactory() + .fromX(0.0f) + .toX(-DISTANCE) + .build(); + } + return sOpenExit; + } + + /** @hide */ + public static Animation getCloseEnter() { + if (sCloseEnter == null) { + sCloseEnter = new ActivityAnimFactory() + .fromX(-DISTANCE) + .toX(0.0f) + .build(); + } + return sCloseEnter; + } + + /** @hide */ + public static Animation getCloseExit() { + if (sCloseExit == null) { + sCloseExit = new ActivityAnimFactory() + .fromX(0.0f) + .toX(1.0f) + .build(); + } + return sCloseExit; + } + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 2aa28894b3ba5..bb1f7e3b801ee 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -941,6 +941,10 @@ android:knownCerts="@array/config_setContactsDefaultAccountKnownSigners" android:featureFlag="android.provider.new_default_account_api_enabled"/> + + + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 127ca3c989840..61283953dc577 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -32,6 +32,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; @@ -54,6 +55,7 @@ import android.window.DesktopModeFlags; import android.window.WindowContainerTransaction; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; @@ -220,7 +222,7 @@ static void updateRelayoutParams( boolean shouldSetTaskVisibilityPositionAndCrop, boolean isStatusBarVisible, boolean isKeyguardVisibleAndOccluded, - InsetsState displayInsetsState, + DisplayController displayController, boolean hasGlobalFocus, @NonNull Region globalExclusionRegion, boolean shouldSetBackground, @@ -246,6 +248,8 @@ static void updateRelayoutParams( || (isStatusBarVisible && !isKeyguardVisibleAndOccluded); relayoutParams.mDisplayExclusionRegion.set(globalExclusionRegion); relayoutParams.mInSyncWithTransition = inSyncWithTransition; + relayoutParams.mCornerRadius = + getCornerRadius(context, displayController.getDisplay(taskInfo.displayId)); if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { // If the app is requesting to customize the caption bar, allow input to fall @@ -265,7 +269,8 @@ static void updateRelayoutParams( ); relayoutParams.mCaptionTopPadding = getTopPadding(relayoutParams, - taskInfo.getConfiguration().windowConfiguration.getBounds(), displayInsetsState); + taskInfo.getConfiguration().windowConfiguration.getBounds(), + displayController.getInsetsState(taskInfo.displayId)); // Set opaque background for all freeform tasks to prevent freeform tasks below // from being visible if freeform task window above is translucent. // Otherwise if fluid resize is enabled, add a background to freeform tasks. @@ -290,7 +295,7 @@ void relayout(RunningTaskInfo taskInfo, updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, - mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus, + mDisplayController, hasGlobalFocus, globalExclusionRegion, mDesktopConfig.shouldSetBackground(taskInfo), inSyncWithTransition); @@ -350,6 +355,19 @@ void relayout(RunningTaskInfo taskInfo, }); } + private static int getCornerRadius(Context context, Display display) { + // Show rounded corners only on the internal display as we can't get rounded corners for + // external displays. + if (display.getType() != Display.TYPE_INTERNAL) { + return 0; + } + final TypedArray ta = context.obtainStyledAttributes( + new int[]{android.R.attr.dialogCornerRadius}); + final int cornerRadius = ta.getDimensionPixelSize(0, 0); + ta.recycle(); + return cornerRadius; + } + /** * Sets up listeners when a new root view is created. */ diff --git a/services/Android.bp b/services/Android.bp index 6f978252e57c4..d22024fd7e5e6 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -133,6 +133,7 @@ filegroup { ":services.coverage-sources", ":services.credentials-sources", ":services.devicepolicy-sources", + ":services.freeform-sources", ":services.midi-sources", ":services.musicsearch-sources", ":services.net-sources", @@ -339,6 +340,7 @@ java_library { "services.credentials", "services.devicepolicy", "services.flags", + "services.freeform", "services.midi", "services.musicsearch", "services.net", diff --git a/services/core/Android.bp b/services/core/Android.bp index 71091f230ad1f..77abaa02e1334 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -134,6 +134,7 @@ java_library_static { ":display-layout-config", ":display-topology", ":device-state-config", + ":lmofreeform-display-adapter-java", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/wm/EventLogTags.logtags", diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index e032b4e332007..f38425c326fe2 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -208,6 +208,8 @@ import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; +import com.libremobileos.freeform.ILMOFreeformDisplayCallback; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -563,6 +565,8 @@ public synchronized void requestDisplayState(int displayId, int state, float bri // only be used for the devices in projected mode. private boolean mIncludeDefaultDisplayInTopology; + private LMOFreeformDisplayAdapter mFreeformDisplayAdapter; + private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -2386,6 +2390,7 @@ private void registerAdditionalDisplayAdapters() { if (shouldRegisterNonEssentialDisplayAdaptersLocked()) { registerOverlayDisplayAdapterLocked(); registerWifiDisplayAdapterLocked(); + registerFreeformDisplayAdapterLocked(); } } } @@ -2406,6 +2411,13 @@ private void registerWifiDisplayAdapterLocked() { } } + private void registerFreeformDisplayAdapterLocked() { + mFreeformDisplayAdapter = new LMOFreeformDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mLogicalDisplayMapper, + mUiHandler, mFlags); + registerDisplayAdapterLocked(mFreeformDisplayAdapter); + } + private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() { // In safe mode, we disable non-essential display adapters to give the user // an opportunity to fix broken settings or other problems that might affect @@ -6431,6 +6443,23 @@ public void reloadTopologies(final int userId) { scheduleTopologiesReload(mCurrentUserId, /*isUserSwitching=*/ false); } } + + public void createFreeformLocked(String name, ILMOFreeformDisplayCallback callback, + int width, int height, int densityDpi, boolean secure, boolean ownContentOnly, + boolean shouldShowSystemDecorations, Surface surface, float refreshRate, + long presentationDeadlineNanos) { + mFreeformDisplayAdapter.createFreeformLocked(name, callback, width, height, densityDpi, + secure, ownContentOnly, shouldShowSystemDecorations, surface, refreshRate, + presentationDeadlineNanos); + } + + public void resizeFreeform(IBinder appToken, int width, int height, int densityDpi) { + mFreeformDisplayAdapter.resizeFreeform(appToken, width, height, densityDpi); + } + + public void releaseFreeform(IBinder appToken) { + mFreeformDisplayAdapter.releaseFreeform(appToken); + } } class DesiredDisplayModeSpecsObserver diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index ac8b92854e68b..8c458ae6f6a42 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -3070,6 +3070,12 @@ int startActivityFromRecents(int callingPid, int callingUid, int taskId, mService.continueWindowLayout(); } } + if (activityOptions != null) { + final int windowingMode = activityOptions.getLaunchWindowingMode(); + if (windowingMode == WINDOWING_MODE_FREEFORM) { + task.setBounds(activityOptions.getLaunchBounds()); + } + } taskCallingUid = task.mCallingUid; callingPackage = task.mCallingPackage; callingFeatureId = task.mCallingFeatureId; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 27427724f0c3e..3b79e0650ee93 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -130,6 +130,7 @@ import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_SLEEP_TOKEN; import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS; import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN; +import static com.android.server.display.LMOFreeformDisplayAdapter.UNIQUE_ID_PREFIX; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; @@ -563,6 +564,8 @@ public void onIdMatch(InsetsSource source1, InsetsSource source2) { // TODO(multi-display): remove some of the usages. boolean isDefaultDisplay; + private boolean isFreeformDisplay; + /** Save allocating when calculating rects */ private final Rect mTmpRect = new Rect(); private final Region mTmpRegion = new Region(); @@ -796,6 +799,8 @@ public void onIdMatch(InsetsSource source1, InsetsSource source2) { /** Last window to hold the screen locked. */ private WindowState mLastWakeLockHoldingWindow; + private boolean mHasSecureContent; + /** * Whether display is allowed to ignore all activity size restrictions. * @see #isDisplayIgnoreActivitySizeRestrictions @@ -1169,6 +1174,7 @@ public void onLowMemory() { mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp * mDisplayMetrics.densityDpi / DENSITY_DEFAULT; isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; + isFreeformDisplay = mDisplayInfo.uniqueId.startsWith(UNIQUE_ID_PREFIX); mInsetsStateController = new InsetsStateController(this); initializeDisplayBaseInfo(); mDisplayFrames = new DisplayFrames(mInsetsStateController.getRawInsetsState(), @@ -2147,7 +2153,9 @@ void continueUpdateOrientationForDiffOrienLaunchingApp() { return; } // The orientation of display is not changed. - clearFixedRotationLaunchingApp(); + if (!mTransitionController.isCollecting(this)) { + clearFixedRotationLaunchingApp(); + } } /** @@ -4362,6 +4370,11 @@ InsetsControlTarget getImeControlTarget() { return mWmService.mDisplayWindowSettings.getImePolicyLocked(this); } + boolean forceDesktopMode() { + return ("VNC".equals(mDisplay.getName()) || mWmService.mForceDesktopModeOnExternalDisplays) + && !isDefaultDisplay && !isFreeformDisplay && !isPrivate(); + } + /** @see WindowManagerInternal#onToggleImeRequested */ void onShowImeRequested() { if (mInputMethodWindow == null) { @@ -5151,6 +5164,11 @@ boolean isInputMethodClientFocus(int uid, int pid) { return imeLayeringTarget.mSession.mUid == uid && imeLayeringTarget.mSession.mPid == pid; } + boolean hasSecureWindowOnScreen() { + final WindowState win = getWindow(w -> w.isOnScreen() && w.isSecureLocked()); + return win != null; + } + // TODO: Super unexpected long method that should be broken down... void applySurfaceChangesTransaction() { final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; @@ -5286,6 +5304,13 @@ private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) { if (updateInputWindows) { mInputMonitor.updateInputWindowsLw(false /*force*/); } + + // Notify if display added or removed a secure window + final boolean hasSecureContent = hasSecureWindowOnScreen(); + if (hasSecureContent != mHasSecureContent) { + mHasSecureContent = hasSecureContent; + mWmService.notifyDisplaySecureContentChange(mDisplayId, hasSecureContent); + } } /** @@ -5780,7 +5805,7 @@ && isPublicSecondaryDisplayWithDesktopModeForceEnabled()) { * also check {@link #isSystemDecorationsSupported()} to avoid breaking any security policy. */ boolean isPublicSecondaryDisplayWithDesktopModeForceEnabled() { - if (!mWmService.mForceDesktopModeOnExternalDisplays || isDefaultDisplay || isPrivate()) { + if (!mWmService.mForceDesktopModeOnExternalDisplays || isDefaultDisplay || isPrivate() && !isFreeformDisplay) { return false; } if (!isWindowingModeSupported(WINDOWING_MODE_FREEFORM)) { diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index bfbdef1a92cef..9d24cba7b9ba1 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -259,16 +259,13 @@ public int getPointerLayer() { @Override public int getPointerDisplayId() { synchronized (mService.mGlobalLock) { - // If desktop mode is not enabled, show on the default display. - if (!mService.mForceDesktopModeOnExternalDisplays) { - return DEFAULT_DISPLAY; - } - // Look for the topmost freeform display. int firstExternalDisplayId = DEFAULT_DISPLAY; for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) { final DisplayContent displayContent = mService.mRoot.mChildren.get(i); - if (displayContent.getDisplayInfo().state == Display.STATE_OFF) { + if (displayContent.getDisplayInfo().state == Display.STATE_OFF + || !displayContent.forceDesktopMode()) { + // If desktop mode is not enabled, show on the default display. continue; } // Heuristic solution here. Currently when "Freeform windows" developer option is diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 8931af7c0474c..4216ada8dd65c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -439,6 +439,14 @@ default void dragRecipientEntered(IWindow window) {} default void dragRecipientExited(IWindow window) {} } + /** + * Listener interface for secure content showing up on the display. + */ + public interface DisplaySecureContentListener { + public void onDisplayHasSecureWindowOnScreenChanged( + int displayId, boolean hasSecureWindowOnScreen); + } + /** * Request the interface to access features implemented by AccessibilityController. */ @@ -1249,4 +1257,12 @@ public abstract void requestAssistScreenshot(IAssistDataReceiver receiver, * @throws RuntimeException if the payload cannot be written to the settings file. */ public abstract void restoreDisplayWindowSettings(int userId, byte[] payload); + + /** + * Register/unregister callbacks for secure content showing up on the display. + */ + public abstract void registerDisplaySecureContentListener( + DisplaySecureContentListener listener); + public abstract void unregisterDisplaySecureContentListener( + DisplaySecureContentListener listener); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index dd4c992494b0a..20a67fa901604 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -797,6 +797,9 @@ public void binderDied() { WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; WindowManagerInternal.OnImeRequestedChangedListener mOnImeRequestedChangedListener; + private ArraySet + mDisplaySecureContentListeners = new ArraySet<>(); + SettingsObserver mSettingsObserver; final EmbeddedWindowController mEmbeddedWindowController; final AnrController mAnrController; @@ -5817,6 +5820,15 @@ void notifyHardKeyboardStatusChange() { } } + void notifyDisplaySecureContentChange(int displayId, boolean hasSecureWindowOnScreen) { + synchronized (mGlobalLock) { + mDisplaySecureContentListeners.forEach((listener) -> { + listener.onDisplayHasSecureWindowOnScreenChanged( + displayId, hasSecureWindowOnScreen); + }); + } + } + // ------------------------------------------------------------- // Input Events and Focus Management // ------------------------------------------------------------- @@ -9214,6 +9226,20 @@ public void requestAssistScreenshot(IAssistDataReceiver receiver, IBinder activi } WindowManagerService.this.requestAssistScreenshotInternal(receiver, displayId); } + + @Override + public void registerDisplaySecureContentListener(DisplaySecureContentListener listener) { + synchronized (mGlobalLock) { + mDisplaySecureContentListeners.add(listener); + } + } + + @Override + public void unregisterDisplaySecureContentListener(DisplaySecureContentListener listener) { + synchronized (mGlobalLock) { + mDisplaySecureContentListeners.remove(listener); + } + } } /** Called to inform window manager if non-Vr UI shoul be disabled or not. */ diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java index d26616edfb44c..0cdd058a09994 100644 --- a/services/core/java/com/android/server/wm/WindowOrientationListener.java +++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java @@ -1177,6 +1177,10 @@ public void onSensorChanged(SensorEvent event) { return; } } + if (mRotationResolverService == null) { + // Bail out because RotationResolverManagerService wasn't started + return; + } String packageName = null; if (mActivityTaskManagerInternal != null) { diff --git a/services/freeform/Android.bp b/services/freeform/Android.bp new file mode 100644 index 0000000000000..3920451176ca5 --- /dev/null +++ b/services/freeform/Android.bp @@ -0,0 +1,27 @@ +// +// SPDX-FileCopyrightText: 2023 The LibreMobileOS Foundation +// SPDX-FileCopyrightText: 2024-2025 crDroid Android Project +// SPDX-License-Identifier: Apache-2.0 +// + +filegroup { + name: "services.freeform-sources", + srcs: [ + "java/**/*.java", + "java/**/*.kt" + ], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.freeform", + defaults: ["platform_service_defaults"], + srcs: [":services.freeform-sources"], + libs: ["services.core"], + static_libs: [ + "kotlinx_coroutines", + "lmofreeform-server", + ], + jarjar_rules: "jarjar-rules.txt", +} diff --git a/services/freeform/jarjar-rules.txt b/services/freeform/jarjar-rules.txt new file mode 100644 index 0000000000000..a50af24f3dddc --- /dev/null +++ b/services/freeform/jarjar-rules.txt @@ -0,0 +1,2 @@ +rule kotlin.** com.libremobileos.server.jarjar.@0 +rule kotlinx.** com.libremobileos.server.jarjar.@0 diff --git a/services/freeform/java/com/android/server/display/FreeformService.java b/services/freeform/java/com/android/server/display/FreeformService.java new file mode 100644 index 0000000000000..af5c3a5484ecc --- /dev/null +++ b/services/freeform/java/com/android/server/display/FreeformService.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023-2024 LibreMobileOS Foundation + * + * 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; + +import android.content.Context; +import android.hardware.display.DisplayManagerInternal; +import android.util.Slog; + +import com.android.server.SystemService; + +import com.libremobileos.freeform.server.LMOFreeformService; +import com.libremobileos.freeform.server.LMOFreeformServiceHolder; +import com.libremobileos.freeform.server.LMOFreeformUIService; + +public class FreeformService extends SystemService { + + private static final String TAG = "FreeformService"; + + public FreeformService(Context context) { + super(context); + } + + @Override + public void onStart() { + // noop + } + + @Override + public void onBootPhase(@BootPhase int phase) { + if (phase != PHASE_ACTIVITY_MANAGER_READY || isSafeMode()) return; + + Slog.d(TAG, "PHASE_ACTIVITY_MANAGER_READY, going to init!"); + + DisplayManagerInternal displayManager = getLocalService(DisplayManagerInternal.class); + if (displayManager == null) { + Slog.e(TAG, "Cannot init: DisplayManagerInternal is null!"); + return; + } + + LMOFreeformService service = new LMOFreeformService(displayManager); + LMOFreeformUIService uiService = + new LMOFreeformUIService(getContext(), displayManager, service); + LMOFreeformServiceHolder.init(uiService, service); + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index c1bfbb330b96f..cb69348f6cbed 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -175,6 +175,7 @@ import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.devicestate.DeviceStateManagerService; import com.android.server.display.DisplayManagerService; +import com.android.server.display.FreeformService; import com.android.server.display.color.ColorDisplayService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; @@ -2817,6 +2818,10 @@ private void startOtherServices(@NonNull TimingsTraceAndSlog t) { t.traceEnd(); } + t.traceBegin("FreeformService"); + mSystemServiceManager.startService(FreeformService.class); + t.traceEnd(); + if (!isWatch) { // We don't run this on watches as there are no plans to use the data logged // on watch devices.