Android 5.1 Phone MO(去电)流程分析(Framework层)

写在前面的话

本文主要分析MO(去电)的流程,研究的代码是Android 5.1的,目前只关注Framework层,以CDMA为例。
关于应用层的流程,请看《Android 5.1 Phone MO(去电)流程分析(应用层)


这里写图片描述
(如果图片看不清的话,可以右键选择在新标签中打开图片,或者把图片另存到自己电脑再查看。)


http://blog.csdn.net/linyongan


步骤1和2:紧接着应用层的流程,在 TelephonyConnectionService.java 的placeOutgoingConnection方法里调用了phone.dial(),我们这里研究的是CDMA,所以下面会进入 CDMAPhone.java 的dial方法:

public Connection dial (String dialString, int videoState, Bundle extras){
        ...
        return dialInternal(dialString, null, videoState);
}

protected Connection dialInternal (String dialString, UUSInfo uusInfo,int videoState) throws CallStateException {
        // Need to make sure dialString gets parsed properly
        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
        return mCT.dial(newDialString);
}

步骤3和4: mCT是CdmaCallTracker类型的,所以我们进入 CdmaCallTracker.java 的dial方法里:

/**
 * clirMode is one of the CLIR_ constants
 */
Connection dial (String dialString, int clirMode) throws CallStateException {
        // note that this triggers call state changed notif
        clearDisconnected();

        if (!canDial()) {
            throw new CallStateException("cannot dial in current state");
        }

        String origNumber = dialString;
        String operatorIsoContry = mPhone.getSystemProperty(
                TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
        String simIsoContry = mPhone.getSystemProperty(
                TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
        boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry)
                && !TextUtils.isEmpty(simIsoContry)
                && !simIsoContry.equals(operatorIsoContry);
        if (internationalRoaming) {
            if ("us".equals(simIsoContry)) {
                internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry);
            } else if ("vi".equals(simIsoContry)) {
                internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry);
            }
        }
        if (internationalRoaming) {
            dialString = convertNumberIfNecessary(mPhone, dialString);
        }

        String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
        boolean isPhoneInEcmMode = inEcm.equals("true");
        boolean isEmergencyCall =
                PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);

        // Cancel Ecm timer if a second emergency call is originating in Ecm mode
        if (isPhoneInEcmMode && isEmergencyCall) {
            handleEcmTimer(CDMAPhone.CANCEL_ECM_TIMER);
        }

        // We are initiating a call therefore even if we previously
        // didn't know the state (i.e. Generic was true) we now know
        // and therefore can set Generic to false.
        mForegroundCall.setGeneric(false);

        // The new call must be assigned to the foreground call.
        // That call must be idle, so place anything that's
        // there on hold
        if (mForegroundCall.getState() == CdmaCall.State.ACTIVE) {
            return dialThreeWay(dialString);
        }

        mPendingMO = new CdmaConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString),
                this, mForegroundCall);
        mHangupPendingMO = false;

        if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
                || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) {
            // Phone number is invalid
            mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;

            // handlePollCalls() will notice this call not present
            // and will mark it as dropped.
            pollCallsWhenSafe();
        } else {
            // Always unmute when initiating a new call
            setMute(false);

            // Check data call
            disableDataCallInEmergencyCall(dialString);

            // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
            if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
                mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
            } else {
                mPhone.exitEmergencyCallbackMode();
                mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
                mPendingCallClirMode=clirMode;
                mPendingCallInEcm=true;
            }
        }

        if (mNumberConverted) {
            mPendingMO.setConverted(origNumber);
            mNumberConverted = false;
        }

        //更新phone状态
        updatePhoneState();
        //发起phone状态变化通知
        mPhone.notifyPreciseCallStateChanged();
        //返回通话连接
        return mPendingMO;
 }

 /**
  * Obtain a message to use for signalling "invoke getCurrentCalls() when
  * this operation and all other pending operations are complete
  */
 private Message obtainCompleteMessage() {
        return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
 }

 /**
  * Obtain a message to use for signalling "invoke getCurrentCalls() when
  * this operation and all other pending operations are complete
  */
 private Message obtainCompleteMessage(int what) {
        mPendingOperations++;
        mLastRelevantPoll = null;
        mNeedsPoll = true;

        if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
                mPendingOperations + ", needsPoll=" + mNeedsPoll);

        return obtainMessage(what);
}

步骤5:先看 obtainCompleteMessage 方法,这里调用obtainCompleteMessage带一个参数的方法创建了一个消息类型为EVENT_OPERATION_COMPLETE的Message,这个Message也会传入到mCi.dial方法里,在这里是CdmaCallTracker主动向RILJ(mCi是RILJ的实例对象,后面再解释为什么)发送消息,等RILJ处理完之后通过回调通知CdmaCallTracker,CdmaCallTracker就在handleMessage方法处理。


知识点解析:至于 mCi ,它是CommandsInterface类型的,在CdmaCallTracker的构造方法里通过mCi = phone.mCi;获取:

public CdmaCallTracker(CDMAPhone phone) {
        ...
        mCi = phone.mCi;
        ...
}

phone是CDMAPhone类型的,在CDMAPhone的构造方法里并没有关于mCi的定义和创建代码,只有super这一行有联系

public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
            boolean unitTestMode) {
        super("CDMA", notifier, context, ci, unitTestMode);
        ...
}

我们再进入CDMAPhone的父类PhoneBase的构造方法里,

protected PhoneBase(String name, PhoneNotifier notifier, Context context,
                              CommandsInterface ci,boolean unitTestMode) {
        ...
        mCi = ci;
        ...
}      

找到mCi的定义和初始化信息:mCi = ci;ci是在CDMAPhone的构造方法里的传递对象,因此,如果找到CDMAPhone创建phone对象的地方,也许可以找到ci的创建。
我们要找CDMAPhone被调用的地方,(在Source Insight这个编译器中,选中CDMAPhone,然后按快捷键Ctrl+/),我们找到 PhoneFactory.java 的makeDefaultPhone方法里:

 public static void makeDefaultPhone(Context context) {
        ...
        sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
        UiccController.make(context, sCommandsInterface);
        int phoneType = TelephonyManager.getPhoneType(networkMode);
        if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
        Rlog.i(LOG_TAG, "Creating GSMPhone");
        sProxyPhone = new PhoneProxy(
                      new GSMPhone(context,sCommandsInterface, sPhoneNotifier));
    }
}   

在创建Phone对象时,传入RILJ类型的sCommandsInterface对象作为参数,因此,我们可以确定CdmaCallTracker.java中使用mCi.dial进行拨号,其实就是调用了 RIL.java 的dial方法。


步骤6: RIL.java 的dial方法

public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
        rr.mParcel.writeString(address);
        rr.mParcel.writeInt(clirMode);
        if (uusInfo == null) {
            rr.mParcel.writeInt(0); // UUS information is absent
        } else {
            rr.mParcel.writeInt(1); // UUS information is present
            rr.mParcel.writeInt(uusInfo.getType());
            rr.mParcel.writeInt(uusInfo.getDcs());      
            rr.mParcel.writeByteArray(uusInfo.getUserData());
        }
        //打印日志
        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + 
                               requestToString(rr.mRequest));
        send(rr); 
}

打印出来的log:

08-11 09:39:24.313 D/RILJ    ( 2904): [5503]> DIAL

在这里创建了RILRequest 对象rr,将CdmaCallTracker传递过来的消息类型为EVENT_OPERATION_COMPLETE的Message赋值给rr.mResult,然后RILJ向RILD发送了RIL_REQUEST_DIAL请求。
步骤7:等底层处理完之后,会发送RIL_REQUEST_DIAL消息给RILJ,在RILJ的 processSolicited 方法里接收并处理该消息:

private RILRequest processSolicited (Parcel p) {
        ...
        case RIL_REQUEST_DIAL: 
        ret =  responseVoid(p); break;
        ...
        //打印log日志
        if (RILJ_LOGD) riljLog(rr.serialString() + "< " + 
        requestToString(rr.mRequest)
                    + " " + retToString(rr.mRequest, ret));
        if (rr.mResult != null) {
            AsyncResult.forMessage(rr.mResult, null, tr);
            rr.mResult.sendToTarget();//发出handler消息通知
}

步骤8,9,10: rr.mResult 就是在CdmaCallTracker中创建的消息类型为EVENT_OPERATION_COMPLETE的Message,所以调用sendToTarget方法,就会把消息发送给CdmaCallTracker,然后在 CdmaCallTracker.java 的handleMessage方法中有对EVENT_OPERATION_COMPLETE的逻辑处理:

public void
    handleMessage (Message msg) {
        ...
        switch (msg.what) {
           case EVENT_OPERATION_COMPLETE:
                operationComplete();
           break;
   ...
}

private void operationComplete() {
        mPendingOperations--;

        if (DBG_POLL) log("operationComplete: pendingOperations=" +
                mPendingOperations + ", needsPoll=" + mNeedsPoll);

        if (mPendingOperations == 0 && mNeedsPoll) {
            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
            mCi.getCurrentCalls(mLastRelevantPoll);
        } else if (mPendingOperations < 0) {
            // this should never happen
            Rlog.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0");
            mPendingOperations = 0;
        }
}

步骤11和12:在这里通过 obtainMessage 方法生成一个消息类型为EVENT_POLL_CALLS_RESULT的Message并且作为getCurrentCalls方法的参数传递到mCi,mCi是RIL.java的实例对象,这也就回到了 RIL.java 的getCurrentCalls方法里,getCurrentCalls将RIL_REQUEST_GET_CURRENT_CALLS 消息封装成RILRequest 类型并发送。

public void getCurrentCalls (Message result) {
        //注意rr对象的消息类型,后面会用到
        RILRequest rr = RILRequest.obtain(
        RIL_REQUEST_GET_CURRENT_CALLS, result);
        //打印log日志
        if (RILJ_LOGD) 
        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
        send(rr);
}

打印出相应的log是

09-09 17:32:02.179 D/RILJ    ( 2795): [5656]> GET_CURRENT_CALLS

步骤13: RILJ向底层请求查询Call List状态列表,等底层处理完之后,就把结果返回给RILJ,向RILJ发送RIL_REQUEST_GET_CURRENT_CALLS消息,RILJ在 processSolicited 方法里处理它。

private RILRequest processSolicited (Parcel p) {
        ...
        case RIL_REQUEST_GET_CURRENT_CALLS: 
        ret =  responseCallList(p); break;
        ...
        //打印log日志
        if (RILJ_LOGD) riljLog(rr.serialString() + "< " + 
        requestToString(rr.mRequest)
                    + " " + retToString(rr.mRequest, ret));
        if (rr.mResult != null) {
            AsyncResult.forMessage(rr.mResult, null, tr);
            rr.mResult.sendToTarget();//发出handler消息通知
}

步骤14和15:还是跟上面一样, rr.mResult 就是刚刚在CdmaCallTracker中创建的那个消息类型为EVENT_POLL_CALLS_RESULT的Message,所以在 CdmaCallTracker.java 的handleMessage 方法中会有EVENT_POLL_CALLS_RESULT的逻辑处理

public void
    handleMessage (Message msg) {
        ...
        switch (msg.what) {
            case EVENT_POLL_CALLS_RESULT:{
                //打印log日志
                Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
                ar = (AsyncResult)msg.obj;

                if(msg == mLastRelevantPoll) {
                    if(DBG_POLL) log(
                            "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
                    mNeedsPoll = false;
                    mLastRelevantPoll = null;
                    handlePollCalls((AsyncResult)msg.obj);
                }
            }
            break;
   ...
}

步骤16:最后,会进入 handlePollCalls 方法,关于handlePollCalls方法,《 handlePollCalls方法详解 》这篇文章讲得比较详细,对于我们这里,是打电话流程中,第一次查询Call List列表,所以会进入这里:

//conn代表旧的通话连接的基本信息,dc代表新的。出现新的通话连接
if (conn == null && dc != null) {
    //主动发起拨号请求后,第一次查询到Call List后,进入这里
    // Connection appeared in CLCC response that we don't know about
    if (mPendingMO != null && mPendingMO.compareTo(dc)) {

        if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);

        // It's our pending mobile originating call
        mConnections[i] = mPendingMO;
        //把i的值赋值给index
        mPendingMO.mIndex = i;
        mPendingMO.update(dc);
        mPendingMO = null;

        // Someone has already asked to hangup this call
        if (mHangupPendingMO) {
            mHangupPendingMO = false;
            // Re-start Ecm timer when an uncompleted emergency call ends
            if (mIsEcmTimerCanceled) {
                handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
            }

            try {
                if (Phone.DEBUG_PHONE) log(
                        "poll: hangupPendingMO, hangup conn " + i);
                hangup(mConnections[i]);
            } catch (CallStateException ex) {
                Rlog.e(LOG_TAG, "unexpected error on hangup");
            }

            // Do not continue processing this poll
            // Wait for hangup and repoll
            return;
        }
} 

好像也没有多大作用,就是把mPendingMO的值赋值给mConnections和把i的值赋值给mIndex 。

步骤17和18:接着,底层又会上报RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息给RILJ,这是底层主动上报的消息,所以RILJ会在 processUnsolicited 方法里进行处理

private void processUnsolicited (Parcel p) {
    ...
    try {switch(response) {
    ...
    case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: 
    ret =  responseVoid(p); break;
    ...
    }
    switch(response) {
    ...
    case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
         if (RILJ_LOGD) 
         unsljLog(response);//打印log日志
         //发出通知(RegistrantList消息处理机制)
         mCallStateRegistrants.notifyRegistrants(new 
         AsyncResult(null, null, null));
    ...
    }
}

(在Source Insight这个编译器中,选中mCallStateRegistrants,然后按快捷键Ctrl+/),我们找到BaseCommands.java的registerForCallStateChanged方法,接着继续找它的调用者,最后我们来到CdmaCallTracker.java的构造方法里

public CdmaCallTracker(CDMAPhone phone) {
      ...
      mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
      ...
}

发觉是CdmaCallTracker.java向RIL注册了一个EVENT_CALL_STATE_CHANGE类型的Handler消息。
步骤19,20,21:因此,我们在 CdmaCallTracker.java 的handleMessage方法里可以找到响应EVENT_CALL_STATE_CHANGE消息类型的处理逻辑,如下:

 public void
     handleMessage (Message msg) {
     ...
     case EVENT_CALL_STATE_CHANGE:
          //调用父类CallTracker查询Call List方法
          pollCallsWhenSafe();
     break;
     ...
}

protected void pollCallsWhenSafe() {
        ...
        if (checkNoOperationsPending()) {
        //注意mLastRelevantPoll对象的消息类型,后面会用到
            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
            mCi.getCurrentCalls(mLastRelevantPoll);
        }
}

步骤22和23:又是RILJ请求查询Call List状态列表。。。老样子,查完还是交给 CdmaCallTracker去处理。这时候Call的状态还是DIALING
步骤24~27:等Call的状态变成ACTIVE后,底层又会上报RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息给RILJ,然后RILJ又请求查询查询Call List状态列表,CdmaCallTracker又处理。。。
到这里,电话已接通,打电话的流程就这么多了。

下面贴出打电话流程的log片段

09-09 17:32:01.730 D/RILJ    ( 2795): [5655]> DIAL
09-09 17:32:01.810 D/RILJ    ( 2795): [5655]< DIAL 
09-09 17:32:01.935 D/RILJ    ( 2795): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
09-09 17:32:02.179 D/RILJ    ( 2795): [5656]> GET_CURRENT_CALLS
09-09 17:32:02.183 V/RILJ    ( 2795): Incoming UUS : NOT present!
09-09 17:32:02.183 D/RILJ    ( 2795): InCall VoicePrivacy is disabled
09-09 17:32:02.183 D/RILJ    ( 2795): [5656]< GET_CURRENT_CALLS  
[id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 
09-09 17:32:02.196 D/RILJ    ( 2795): [5657]> GET_CURRENT_CALLS
09-09 17:32:02.201 V/RILJ    ( 2795): Incoming UUS : NOT present!
09-09 17:32:02.201 D/RILJ    ( 2795): InCall VoicePrivacy is disabled
09-09 17:32:02.201 D/RILJ    ( 2795): [5657]< GET_CURRENT_CALLS  
[id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 
09-09 17:32:02.306 D/CdmaCallTracker( 2795): Event EVENT_POLL_CALLS_RESULT Received
09-09 17:32:02.306 D/CdmaCallTracker( 2795): Event EVENT_POLL_CALLS_RESULT Received
09-09 17:32:03.607 D/RILJ    ( 2795): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
09-09 17:32:03.686 D/RILJ    ( 2795): [5658]> GET_CURRENT_CALLS
09-09 17:32:03.688 D/RILJ    ( 2795): [5659]> GET_CURRENT_CALLS
09-09 17:32:03.691 D/RilRequest( 2795): [5659]< GET_CURRENT_CALLS 
error: com.android.internal.telephony.CommandException: GENERIC_FAILURE ret=
09-09 17:32:03.696 D/RILJ    ( 2795): [5658]< GET_CURRENT_CALLS  
[id=1,ACTIVE,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 
09-09 17:32:03.745 D/CdmaCallTracker( 2795): Event EVENT_POLL_CALLS_RESULT Received
09-09 17:32:03.745 D/CdmaCallTracker( 2795): Event EVENT_POLL_CALLS_RESULT Received
09-09 17:32:03.995 D/RILJ    ( 2795): [5660]> GET_CURRENT_CALLS
09-09 17:32:04.002 D/RILJ    ( 2795): [5660]< GET_CURRENT_CALLS  
[id=1,ACTIVE,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 
09-09 17:32:04.002 D/CdmaCallTracker( 2795): Event EVENT_POLL_CALLS_RESULT Received
09-09 17:32:04.004 D/CallStateMonitor( 2795): handleMessage(10)
09-09 17:32:04.004 D/CallNotifier( 2795): PHONE_ENHANCED_VP_OFF...
09-09 17:32:04.005 D/CallStateMonitor( 2795): handleMessage(1)

补充:看到上面的log中出现了GET_CURRENT_CALLS
error,对于这种情况的后续处理,请查看《“RILJ多次发出GET_CURRENT_CALLS请求”问题分析

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android 5.1是一个广泛使用的操作系统版本,支持HDMI输出功能。要设置HDMI分辨率,可以按照以下步骤进行: 1. 首先,将Android设备与HDMI显示器或视连接。确保HDMI线缆正常连接。 2. 在Android设备上打开设置菜单。可以通过下拉通知栏或点击主屏幕上的设置图标来访问设置菜单。 3. 在设置菜单中,向下滚动并找到“显示”选项。点击进入显示设置页面。 4. 在显示设置页面中,可能会看到“屏幕投射”或“显示模式”的选项。点击进入相关设置。 5. 在屏幕投射或显示模式设置页面中,可以看到HDMI选项。点击进入HDMI设置。 6. 在HDMI设置页面中,通常会有分辨率选项。点击进入分辨率设置。 7. 在分辨率设置页面中,可以看到可用的分辨率选项列表。根据显示器或视的支持能力和个人偏好,选择所需的分辨率。 8. 选择完分辨率后,点击确认或应用。系统将应用所选的分辨率设置。 9. 返回到上一个菜单或主屏幕,查看是否已成功设置HDMI分辨率。显示器或视上的画面应该会根据所选的分辨率进行调整。 需要注意的是,不同的Android设备或系统版本可能会有略微不同的设置流程。因此,根据具体的设备和系统版本,上述步骤可能会有所变化。但总体而言,通过进入设置菜单,找到显示设置,再进入HDMI设置,选择适当的分辨率,就可以完成Android 5.1设置HDMI分辨率的流程。 ### 回答2: Android 5.1版本的设备在设置HDMI分辨率时,可以按照以下步骤进行操作: 1. 连接HDMI线缆:首先,将一端的HDMI线缆插入Android设备的HDMI输出接口,另一端插入显示设备(如视或投影仪)的HDMI输入接口。 2. 打开设置界面:在Android设备上,找到并点击打开“设置”应用程序,通常可以在应用程序列表中找到该选项。 3. 进入显示设置:在“设置”主界面中,向下滚动并找到“显示”选项,点击进入显示设置界面。 4. 选择HDMI设置:在显示设置界面中,找到并点击“HDMI”选项,这将打开HDMI设置界面。 5. 选择分辨率:在HDMI设置界面中,通常会显示可用的HDMI分辨率选项。根据你的显示设备和个人需求,选择适当的分辨率选项。 6. 保存设置:选择完分辨率后,点击界面上的“保存”或“应用”按钮,以保存并应用新的HDMI分辨率设置。 7. 测试分辨率:你可以通过在显示设备上观察图像是否清晰和完整来测试新的HDMI分辨率设置。如果满意,设置流程就结束了。 请注意,以上步骤仅适用于Android 5.1版本的系统设备,不同的Android版本或设备类型可能略有不同。确保你的设备支持HDMI输出功能并运行在Android 5.1版本或更高版本。 ### 回答3: 要设置Android 5.1的HDMI分辨率,可以按照以下流程操作: 1. 首先,确保你的Android设备已连接到HDMI显示器上。 2. 在设备上滑动屏幕,进入主菜单,找到并点击“设置”图标。 3. 在设置菜单中,向下滚动找到“显示”或类似的选项,并点击进入。 4. 在显示设置菜单中,找到“屏幕分辨率”或类似的选项,并点击进入。 5. 系统会列出可用的屏幕分辨率选项。根据你的需求和HDMI显示器的能力,选择一个适合的分辨率。 6. 点击选中分辨率后,系统会提示你是否确认应用此分辨率。确认后,系统会应用新的分辨率设置。 7. 返回到主菜单或桌面,你会注意到HDMI显示器的分辨率已经改变为你所设置的分辨率。 需要注意的是,不同的Android设备可能会在设置菜单的布局和选项名称上有所区别,具体操作可能会有所不同。但一般来说,通过“设置”-“显示”-“屏幕分辨率”可以找到相关的选项。 如果你在Android设备上找不到上述选项,可能是因为该设备的固件版本或制造商定制的界面导致了菜单布局的差异。在这种情况下,你可能需要查阅设备的用户手册或进行在线搜索来了解具体的设置方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值