锁屏状态下NFC不可用
1. Description
在工厂测试软件中,需要对nfc进行测试。
1. 开机启动后,直接进入工厂测试软件中
2. 运行到nfc测试用例时,发现该测试用例不能成功的检测到nfc
3. 退出工厂测试软件,进入setttings->connected devices->打开nfc开关,继续验证nfc,发现是OK的。
4. 在回到工厂测试软件中nfc测试用例,nfc测试又变为ok。
2. Analysis
经过分析发现,在开机启动后,直接进入工厂测试软件时,设备是在锁屏后才进入工厂测试软件的。这就导致当前设备是锁屏的状态,而工厂测试软件又将该锁屏界面给覆盖了,所有导致在进行nfc测试用例时,nfc是打不开的。
然后问题就转为在进行工厂软件测试时,通过该软件自动解锁后在进行nfc测试用例。
然后网上查看相关资料发现,在NfcService.java中其实是可以修改NFC最小enable状态的,对应的属性为:
// minimum screen state that enables NFC polling
static final int NFC_POLLING_MODE = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
而属性 NFC_POLLING_MODE分别有一下几种状态可以使用:
SCREEN_STATE_OFF黑屏状态
SCREEN_STATE_ON_LOCKED屏幕亮了,但是是锁屏状态
SCREEN_STATE_ON_UNLOCKED 屏幕亮了,并且是解锁状态
因此该问题可以有4种方式解决:
在framework层进行修改,将属性 NFC_POLLING_MODE设为SCREEN_STATE_ON_LOCKED,这样即使在锁屏状态下,nfc还是可以enable的。
在nfc本身的测试用例中,加入解锁的逻辑,具体如下所示:
public static void wakeUpAndUnlock(Context context) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
boolean screenOn = pm.isScreenOn();
if (!screenOn) {
PowerManager.WakeLock mWakeLock = pm.newWakeLock(
PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "RunInWakeUp");
mWakeLock.acquire(10000);
mWakeLock.setReferenceCounted(false);
mWakeLock.release();
}
KeyguardManager keyguardManager = (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
KeyguardManager.KeyguardLock keyguardLock = keyguardManager
.newKeyguardLock("unLock");
keyguardLock.reenableKeyguard();
keyguardLock.disableKeyguard();
}
- 还可以使用settings中的关闭锁屏功能的逻辑,即在整个版本中不锁屏(这个修改其实影响比较大,因为它是针对整个系统的,如果一直不让系统锁屏,功耗方面就是第一个大问题)。
<!-- Is the lock-screen disabled for new users by default -->
<bool name="config_disableLockscreenByDefault">false</bool>
如果将配置文件中config_disableLockscreenByDefault
的值改为true,则在熄屏后默认不进行锁屏。
但是直接在此处改,不管编译什么版本都是会变成熄屏后默认不进行锁屏的逻辑,而当前的需求只是在工厂版本需要这个功能,那就可以在framework层动态修改。 - 在锁屏的时候让锁屏界面不显示,需要修改文件
framework/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
和文件frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
,具体修改如下。
/**KeyguardViewMediator.java文件
* Enable the keyguard if the settings are appropriate.
*/
private void doKeyguardLocked(Bundle options) {
if (KeyguardUpdateMonitor.CORE_APPS_ONLY) {
// Don't show keyguard during half-booted cryptkeeper stage.
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because booting to cryptkeeper");
return;
}
// if another app is disabling us, don't show
if (!mExternallyEnabled) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
// note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
// for an occasional ugly flicker in this situation:
// 1) receive a call with the screen on (no keyguard) or make a call
// 2) screen times out
// 3) user hits key to turn screen back on
// instead, we reenable the keyguard when we know the screen is off and the call
// ends (see the broadcast receiver below)
// TODO: clean this up when we have better support at the window manager level
// for apps that wish to be on top of the keyguard
return;
}
// if the keyguard is already showing, don't bother
if (mStatusBarKeyguardViewManager.isShowing()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
resetStateLocked();
return;
}
// In split system user mode, we never unlock system user.
if (!mustNotUnlockCurrentUser()
|| !mUpdateMonitor.isDeviceProvisioned()) {
// if the setup wizard hasn't run yet, don't show
final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
final boolean absent = SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(ABSENT));
final boolean disabled = SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
|| ((absent || disabled) && requireSim);
if (!lockedOrMissing && shouldWaitForProvisioning()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+ " and the sim is not locked or missing");
return;
}
boolean forceShow = options != null && options.getBoolean(OPTION_FORCE_SHOW, false);
if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
&& !lockedOrMissing && !forceShow) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
return;
}
if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) {
if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
// Without this, settings is not enabled until the lock screen first appears
setShowingLocked(false);
hideLocked();
return;
}
}
/*modify for nfc function ,not showing lockscreen*/
if (SystemProperties.getBoolean("ro.dev.MMITest", false)){
Log.d(TAG, "doKeyguard: Jrdminisw mode, not showing lockscreen ");
return ;
}
/*modify return before lock device screen*/
if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
showLocked(options);
}
//PhoneWindowManager.java文件
public void systemReady() {
// In normal flow, systemReady is called before other system services are ready.
// So it is better not to bind keyguard here.
/*modify for nfc function ,not verify SystemReady is MMITest version*/
if (!SystemProperties.getBoolean("ro.dev.MMITest", false)) {
// mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
}
/*modify for nfc function */
mVrManagerInternal = LocalServices.getService(VrManagerInternal.class);
if (mVrManagerInternal != null) {
mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
}
readCameraLensCoverState();
updateUiMode();
synchronized (mLock) {
updateOrientationListenerLp();
mSystemReady = true;
mHandler.post(new Runnable() {
@Override
public void run() {
updateSettings();
}
});
// If this happens, for whatever reason, systemReady came later than systemBooted.
// And keyguard should be already bound from systemBooted
if (mSystemBooted) {
mKeyguardDelegate.onBootCompleted();
}
}
mSystemGestures.systemReady();
mImmersiveModeConfirmation.systemReady();
}
3. Solution
最后解决方法还是选择了第三种,对文件frameworks/base/core/java/com/android/internal/widget/LockPatternUtils.java
具体修改如下:
/**
* Determine if LockScreen is disabled for the current user. This is used to decide whether
* LockScreen is shown after reboot or after screen timeout / short press on power.
*
* @return true if lock screen is disabled
*/
public boolean isLockScreenDisabled(int userId) {
if (isSecure(userId)) {
return false;
}
boolean disabledByDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_disableLockscreenByDefault);
/*modify for nfc , disable Lockscreen ByDefault */
Log.d(TAG, "hello,disabledByDefault is : "+disabledByDefault);
if (SystemProperties.getBoolean("ro.dev.MMITest", false)){
Log.d(TAG, "hello,dev.t2m.MMITest is true,disable Lockscreen By Default is:"+!disabledByDefault);
disabledByDefault = !disabledByDefault;
}
/*modify for nfc , disable Lockscreen ByDefault*/
boolean isSystemUser = UserManager.isSplitSystemUser() && userId == UserHandle.USER_SYSTEM;
UserInfo userInfo = getUserManager().getUserInfo(userId);
boolean isDemoUser = UserManager.isDeviceInDemoMode(mContext) && userInfo != null
&& userInfo.isDemo();
return getBoolean(DISABLE_LOCKSCREEN_KEY, false, userId)
|| (disabledByDefault && !isSystemUser)
|| isDemoUser;
}
4. Summary
这个问题最根本的点其实就是锁屏下nfc不可用导致的,只要找到这个点,解起来方法还是比较多的,然后就是需要注意区分cu版本和工厂测试版本,在进行修改的时候,不能影响cu版本的正常逻辑。