음악을 먼저 재생할 때 adb 명령을 실행합니다.
adb shell dumpsys power|grep -i wake
NetEase Cloud Music을 예로 들어 보겠습니다.
C:\Users\Administrator>adb shell dumpsys power| findstr -i wake
no_cached_wake_locks=true
mWakefulness=Awake
mWakefulnessChanging=false
mWakeLockSummary=0x1
mLastWakeTime=187182227 (24697 ms ago)
mHoldingWakeLockSuspendBlocker=true
mWakeUpWhenPluggedOrUnpluggedConfig=true
mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
mDrawWakeLockOverrideFromSidekick=false
mDoubleTapWakeEnabled=false
Wake Locks: size=2
PARTIAL_WAKE_LOCK 'AudioMixAudioOut_D' ACQ=-831ms (uid=1041 ws=WorkSource{10139})
PARTIAL_WAKE_LOCK 'com.netease.cloudmusic.module.player.NeteaseAudioPlayer' ON_AFTER_RELEASE ACQ=-1s168ms (uid=10139 pid=26062)
PowerManagerService.WakeLocks: ref count=1
wakeLock의 태그는 "AudioMixAudioOut_D"입니다.이 태그를 사용하여 음악 응용 프로그램인지 확인한 다음 음악 재생 여부를 확인하는 방법은 무엇입니까? 분석을 시작하겠습니다.
framewroks \ base \ core \ java \ android \ os \ PowerManagerInternal.java에는 내부 인터페이스 PowerControllerInternalCallback이 있습니다.
public interface PowerControllerInternalCallback {
public void onWakeLockAcquired(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource);
public void onWakeLockReleased(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource);
public void onWakeLockChanging(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource, String newTag,
String newPackageName, int newOwnerUid, int newOwnerPid, WorkSource newWorkSource);
public void onConstraintAppAcquireWakeLock(long nowElapsed, long wakelockStartTime);
}
Spreadtrum의 절전 기능을 예로 들어 보겠습니다.
먼저 PowerControllerInternalCallback 인터페이스를 구현합니다.
frameworks \ base \ services \ core \ java \ com \ android \ server \ power \ sprdpower \ WakelockConstraintHelper.java
private class WakeLockObserver
implements PowerManagerInternal.PowerControllerInternalCallback {
@Override
public void onWakeLockAcquired(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource) {
noteAudioWakeLockAcquired(tag, packageName, ownerUid, ownerPid, workSource);
}
@Override
public void onWakeLockReleased(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource) {
noteAudioWakeLockReleased(tag, packageName, ownerUid, ownerPid, workSource);
}
@Override
public void onWakeLockChanging(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource,String newTag,
String newPackageName, int newOwnerUid, int newOwnerPid, WorkSource newWorkSource) {
noteAudioWakeLockChanging(tag, packageName,
ownerUid, ownerPid, workSource, newTag,
newPackageName, newOwnerUid, newOwnerPid, newWorkSource);
}
@Override
public void onConstraintAppAcquireWakeLock(long nowElapsed, long wakelockStartTime) {
noteConstraintAppAcquireWakeLock(nowElapsed, wakelockStartTime);
}
}
그런 다음이 WakeLockObserver를 PowerManagerInternal에 등록합니다.
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
......
mLocalPowerManager.registerPowerControllerInternalCallback(mWakeLockObserver);
PowerManagerInternal.java는 추상 클래스이며 PowerManagerService.java의 내부 클래스 LocalService는 PowerManagerInternal을 상속합니다.
이러한 방식으로 시스템이 wakelock 애플리케이션이 있음을 감지하면 onWakeLockAcquired () 메서드를 호출합니다.
앱 계층에서 WakeLock을 획득하는 일반적인 프로세스는 다음과 같습니다.
1. frameworks/base/core/java/android/os/PowerManager.java
acquire--->acquireLocked---->PowerManagerService.acquireWakeLock
2. frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
-->acquireWakeLock
-->acquireWakeLockInternal
-->updatePowerStateLocked
-->updateSuspendBlockerLocked
-->updatePowerStateLocked
-->updateSuspendBlockerLocked
-->mWakeLockSuspendBlocker.acquire
-->PowerManagerService$SuspendBlockerImpl.acquire-->nativeAcquireSuspendBlocker
3. frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
nativeAcquireSuspendBlocker-->acquire_wake_lock
테마로 돌아 가기
PowerManagerService.java-> acquireWakeLockInternal ()은 notifyWakeLockAcquiredLocked ()가 필요한 메소드를 호출합니다.
private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
if (mSystemReady && !wakeLock.mDisabled) {
wakeLock.mNotifiedAcquired = true;
mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
wakeLock.mHistoryTag);
restartNofifyLongTimerLocked(wakeLock);
// NOTE: Bug #627645 low power Feature BEG-->
// notify listeners
mPowerControllerHelper.notifyWakeLockAcquiredLocked(wakeLock);
// <-- NOTE: Bug #627645 low power Feature END
}
}
PowerControllerHelper는 PowerManagerService.java에서 Spreadtrum에 의해 추가 된 내부 클래스입니다.
PowerControllerHelper-> notifyWakeLockAcquiredLocked ()
public void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
if (wakeLock.mStartAcquireTimeStamp <= 0) {
wakeLock.mStartAcquireTimeStamp = SystemClock.elapsedRealtime();
} else {
Slog.d(TAG, "call notifyWakeLockAcquiredLocked > 1, wake lock:" + wakeLock.mTag
+ " from " + wakeLock.mPackageName + "(" + wakeLock.mOwnerUid +")");
}
// if Audio acquired a wakelock notify listeners
if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) == PowerManager.PARTIAL_WAKE_LOCK
&& Process.AUDIOSERVER_UID == wakeLock.mOwnerUid) {
if (mPowerControllerInternalCallback != null) {
mPowerControllerInternalCallback.onWakeLockAcquired(wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
}
}
}
여기서 Lock의 레벨이 PARTIAL_WAKE_LOCK이라고 판단하고 프로세스 uid가 AUDIOSERVER_UID인지 판단합니다.
/**
* Defines the UID/GID for the audioserver process.
* @hide
*/
public static final int AUDIOSERVER_UID = 1041;
먼저 WakeLock levelAndFlags 및 사용 시나리오에 대한 설명을 추가합니다.
이 네 가지 레벨 외에도 PowerMager는 레벨과 함께 사용할 수있는 두 개의 플래그를 제공합니다.
이제 notifyWakeLockAcquiredLocked ()를 다시 살펴 보겠습니다 .PARTIAL_WAKE_LOCK과 AUDIOSERVER_UID의 판단 조건이 충족되면 위에서 언급 한 PowerControllerInternalCallback 인터페이스의 onWakeLockAcquired () 메서드가 호출됩니다.
이제 WakelockConstraintHelper.java의 onWakeLockAcquired ()로 이동합니다.
public void onWakeLockAcquired(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource) {
noteAudioWakeLockAcquired(tag, packageName, ownerUid, ownerPid, workSource);
}
private void noteAudioWakeLockAcquired(String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource) {
if (DEBUG) Slog.d(TAG, "noteAudioWakeLockAcquired: workSource:" + workSource);
// only care about the workSource
if (workSource == null) return;
if (!AUDIO_PACKAGE_NAME.equals(packageName) || Process.AUDIOSERVER_UID != ownerUid) return;
//try {
ArrayList<Integer> uids = new ArrayList();
if (workSource != null) {
int num = workSource.size();
int count = 0;
for (; count<num; count++) {
uids.add(workSource.get(count));
}
}
if (DEBUG) Slog.d(TAG, "noteAudioWakeLockAcquired: tag: " + tag + " uid size:" + uids.size());
if (isAudioIn(tag)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_AUDIOIN_WAKELOCK_UPDATED, uids));
} else if (isAudioOut(tag)) {
WakeLockInfo wakeLockInfo = new WakeLockInfo(tag, packageName, ownerUid, workSource);
mHandler.sendMessage(mHandler.obtainMessage(MSG_AUDIOOUT_WAKELOCK_UPDATED, wakeLockInfo));
}
//} catch (Exception e) {}
}
case MSG_AUDIOOUT_WAKELOCK_UPDATED:
handleAudioOutWakeLockUpdated((WakeLockInfo)msg.obj);
break;
private void handleAudioOutWakeLockUpdated(WakeLockInfo wakeLockInfo) {
......
appState.updateAudioState(audioFlag);
......
}
public void updateAudioState(int audioState) {
if (mAudioFlag != audioState
&& (audioState & AUDIO_TYPE_OUT) != 0) {
mLastTimePlayingMusicSeen = SystemClock.elapsedRealtime();
}
mAudioFlag = audioState;
}
public boolean isPlayingMusic() {
return (mAudioFlag & AUDIO_TYPE_OUT) != 0;
}
음악 재생 여부 판단이 필요한 경우이 mAudioFlag와 AudioManager의 isMusicActive ()를 결합하여 음악 재생 여부 판단
private boolean isPlayingMusicInternal(AppState appState) {
if (appState.mPlayingMusic) return true;
if(mAudioManager != null && !mAudioManager.isMusicActive()
/*&& !mAudioManager.isFmActive()*/){
return false;
}
if (DEBUG_MORE) Slog.d(TAG, "mAudioManager.isMusicActive(): " + mAudioManager.isMusicActive()
+ " getMode():" + mAudioManager.getMode());
boolean playing = appState.isPlayingMusic();
// if app is still playing music after system standby for APP_PLAYING_MUSIC_THRESHOLD
// set app to be playing music
if (playing && mStandbyStartTime > 0) {
long standbyDuration = SystemClock.elapsedRealtime() - mStandbyStartTime;
if (standbyDuration > APP_PLAYING_MUSIC_THRESHOLD)
appState.setPlayingMusicState(true);
}
return playing;
}