辅助设置中自动旋转

本文深入探讨了Android系统中自动旋转功能的实现原理,从点击自动旋转按钮开始,逐步跟踪到核心函数`setRotationLockForAccessibility`,并详细分析了其内部逻辑及与之交互的关键组件如`WindowManagerService`的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

补充:4.4.4bug已经被修改

切入正题

AccessibilitySettings.java中,点击自动旋转的调用以下函数

private void handleLockScreenRotationPreferenceClick() {
        RotationPolicy.setRotationLockForAccessibility(getActivity(),
                !mToggleLockScreenRotationPreference.isChecked());
}

很自然的跟踪到RotationPolicy.java

public static void setRotationLockForAccessibility(Context context, final boolean enabled) {
        Settings.System.putIntForUser(context.getContentResolver(),
                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,
                        UserHandle.USER_CURRENT);
 
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
                    if (enabled) {
                        wm.freezeRotation(Surface.ROTATION_0);
                    } else {
                        wm.thawRotation();
                    }
                } catch (RemoteException exc) {
                    Log.w(TAG, "Unable to save auto-rotate setting");
                }
            }
        });
}


 

这里首先保存了设置的状态。然后,就根据状态做相应的设置。

1.     wm.freezeRotation(Surface.ROTATION_0);强制定死一个方向不旋转

2.     wm.thawRotation();我们重点看这个

 

 

WindowManagerService.java中看到这个函数

    public void thawRotation() {
        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                "thawRotation()")) {
            throw new SecurityException("Requires SET_ORIENTATION permission");
        }
 
        if (DEBUG_ORIENTATION) Slog.v(TAG, "thawRotation: mRotation=" + mRotation);
 
        mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 777); // rot not used
        updateRotationUnchecked(false, false);
}

 

关键点来了:mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 777); // rot not used

看样子这个是主要部分。控制着旋转

 

updateRotationUnchecked(false, false);看起来是个状态更新。到时候更下去看看

 

 

紧跟着PhoneWindowManager.java

public void setUserRotationMode(int mode, int rot) {
        ContentResolver res = mContext.getContentResolver();
 
        // mUserRotationMode and mUserRotation will be assigned by the content observer
        if (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
            Settings.System.putIntForUser(res,
                    Settings.System.USER_ROTATION,
                    rot,
                    UserHandle.USER_CURRENT);
            Settings.System.putIntForUser(res,
                    Settings.System.ACCELEROMETER_ROTATION,
                    0,
                    UserHandle.USER_CURRENT);
        } else {
            Settings.System.putIntForUser(res,
                    Settings.System.ACCELEROMETER_ROTATION,
                    1,
                    UserHandle.USER_CURRENT);
        }
}



这个函数居然也只是状态更新啊。蛋疼。那在哪里做的.

 

 

重新回到WindowManagerService.java分析updateRotationUnchecked

public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
        if(DEBUG_ORIENTATION) Slog.v(TAG, "updateRotationUnchecked("
                   + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")");
 
        long origId = Binder.clearCallingIdentity();
        boolean changed;
        synchronized(mWindowMap) {
            changed = updateRotationUncheckedLocked(false);
            if (!changed || forceRelayout) {
                getDefaultDisplayContentLocked().layoutNeeded = true;
                performLayoutAndPlaceSurfacesLocked();
            }
        }
 
        if (changed || alwaysSendConfiguration) {
            sendNewConfiguration();
        }
 
        Binder.restoreCallingIdentity(origId);
}

 

一句句分析

long origId = Binder.clearCallingIdentity();---à清除调用进程的UIDPID,重置为被调用进程的UIDPID

 

updateRotationUncheckedLockedfalse)又是什么呢

贴源码

// TODO(multidisplay): Rotate any display?
    public boolean updateRotationUncheckedLocked(boolean inTransaction) {
        if (mDeferredRotationPauseCount > 0) {
            // Rotation updates have been paused temporarily.  Defer the update until
            // updates have been resumed.
            if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, rotation is paused.");
            return false;
        }
 
        ScreenRotationAnimation screenRotationAnimation =            mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
//这玩意儿是旋转的动画,mAnimator是singleton,保存了ScreenRotationAnimation,同时,又持有DimSurface,想来这个就是最终要使用的Surface了吧。这里不做详细解释。
        if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
            // Rotation updates cannot be performed while the previous rotation change
            // animation is still in progress.  Skip this update.  We will try updating
            // again after the animation is finished and the display is unfrozen.
            if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, animation in progress.");
            return false;
        }
 
        if (!mDisplayEnabled) {
            // No point choosing a rotation if the display is not enabled.
            if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, display is not enabled.");
            return false;
        }
 
        // TODO: Implement forced rotation changes.
        //       Set mAltOrientation to indicate that the application is receiving
        //       an orientation that has different metrics than it expected.
        //       eg. Portrait instead of Landscape.
 
        int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);//#这里比较重要。后续的计算都基于这个值。看下取到的是什么?
       
        boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
                mForcedAppOrientation, rotation);
 
        if (DEBUG_ORIENTATION) {
            Slog.v(TAG, "Application requested orientation "
                    + mForcedAppOrientation + ", got rotation " + rotation
                    + " which has " + (altOrientation ? "incompatible" : "compatible")
                    + " metrics");
        }
 
        if (mRotation == rotation && mAltOrientation == altOrientation) {
            // No change.
            return false;
        }
 
        if (DEBUG_ORIENTATION) {
            Slog.v(TAG,
                "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "")
                + " from " + mRotation + (mAltOrientation ? " (alt)" : "")
                + ", forceApp=" + mForcedAppOrientation);
        }
 
        mRotation = rotation;
        mAltOrientation = altOrientation;
        mPolicy.setRotationLw(mRotation);//更新旋转状态,同步到WindowOrientationListener中---------------系统默认设置中的旋转bug就在这里没做好。
 
        mWindowsFreezingScreen = true;
        mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
        mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
                WINDOW_FREEZE_TIMEOUT_DURATION);//重新保存window信息
        mWaitingForConfig = true;
        getDefaultDisplayContentLocked().layoutNeeded = true;
        startFreezingDisplayLocked(inTransaction, 0, 0);//保存状态,初始化
        // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
        screenRotationAnimation =
                mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
 
        // We need to update our screen size information to match the new
        // rotation.  Note that this is redundant with the later call to
        // sendNewConfiguration() that must be called after this function
        // returns...  however we need to do the screen size part of that
        // before then so we have the correct size to use when initializing
        // the rotation animation for the new rotation.
        computeScreenConfigurationLocked(null);
 
        final DisplayContent displayContent = getDefaultDisplayContentLocked();
        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
        if (!inTransaction) {
            if (SHOW_TRANSACTIONS) {
                Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked");
            }
            Surface.openTransaction();
        }
        try {
            // NOTE: We disable the rotation in the emulator because
            //       it doesn't support hardware OpenGL emulation yet.
            if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
                    && screenRotationAnimation.hasScreenshot()) {
                if (screenRotationAnimation.setRotationInTransaction(
                        rotation, mFxSession,
                        MAX_ANIMATION_DURATION, mTransitionAnimationScale,
                        displayInfo.logicalWidth, displayInfo.logicalHeight)) {
                    updateLayoutToAnimationLocked();
                }
            }
 
            mDisplayManagerService.performTraversalInTransactionFromWindowManager();
        } finally {
            if (!inTransaction) {
                Surface.closeTransaction();
                if (SHOW_LIGHT_TRANSACTIONS) {
                    Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked");
                }
            }
        }
 
        final WindowList windows = displayContent.getWindowList();
        for (int i = windows.size() - 1; i >= 0; i--) {
            WindowState w = windows.get(i);
            if (w.mHasSurface) {
                if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
                w.mOrientationChanging = true;
                mInnerFields.mOrientationChangeComplete = false;
            }
        }
 
        for (int i=mRotationWatchers.size()-1; i>=0; i--) {
            try {
                mRotationWatchers.get(i).onRotationChanged(rotation);
            } catch (RemoteException e) {
            }
        }
 
        scheduleNotifyRotationChangedIfNeededLocked(displayContent, rotation);
 
        return true;
    }

 

补充下Surface.java中,四个方向的定义

    public static final int ROTATION_0 = 0;
    public static final int ROTATION_90 = 1;
    public static final int ROTATION_180 = 2;
    public static final int ROTATION_270 = 3;

  

int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);//#这里比较重要。后续的计算都基于这个值。看下取到的是什么?

 

切换到PhoneWindowManager.java

@Override
    public int rotationForOrientationLw(int orientation, int lastRotation) {
        if (true) {
            Slog.v("WindowManager", "aaaaaaaaaa  rotationForOrientationLw(orient="
                        + orientation + ", last=" + lastRotation
                        + "); user=" + mUserRotation + " "
                        + ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED)
                            ? "USER_ROTATION_LOCKED" : "")
                        );
        }
 
        synchronized (mLock) {
            int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1
            if (sensorRotation < 0) {
                sensorRotation = lastRotation;
            }
…
…
这部分都是些系统旋转的东西
…
…
}


而这里是getProposedRotation,是在WindowOrientationListener.java中,

直接返回mProposedRotation

 

mPolicy.setRotationLw(mRotation);

更新了这个listener的状态。

那么。WindowOrientationListener.java是何方神圣

跟进去后发现,在disable的状态中,没有重新初始化数字,依旧保存了上次的数值。导致了这个问题

    public void disable() {
        if (mSensor == null) {
            Log.w(TAG, "Cannot detect sensors. Invalid disable");
            return;
        }
        if (mEnabled == true) {
            if (LOG) {
                Log.d(TAG, "WindowOrientationListener disabled");
            }
        mSensorEventListener.reset();////添加这句
            mSensorManager.unregisterListener(mSensorEventListener);
            mEnabled = false;
        }
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值