安卓8.0 WIFI休眠策略分析

http://androidxref.com/8.0.0_r4
============================================================
/frameworks/base/core/java/android/provider/Settings.java

public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";

public static final int WIFI_SLEEP_POLICY_DEFAULT = 0;
public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1;
public static final int WIFI_SLEEP_POLICY_NEVER = 2;

============================================================
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiController.java

private int mSleepPolicy; // 當前休眠策略
private int mPluggedType; // 當前充電狀態  可選值如下
{
private static final int BATTERY_PLUGGED_NONE = 0;  // 手機沒有充電
private static final int BATTERY_PLUGGED_AC = 1; // 註冊充電
private static final int BATTERY_PLUGGED_USB = 2; // 連接電腦USB充電
private static final int BATTERY_PLUGGED_WIRELESS= 4;  // 無線充電
public static final int BATTERY_PLUGGED_ANY = 7;
}


    /**
     * Determines whether the Wi-Fi chipset should stay awake or be put to
     * sleep. Looks at the setting for the sleep policy and the current
     * conditions.  
     *
     * @see #shouldDeviceStayAwake(int)
     */
    private boolean shouldWifiStayAwake(int pluggedType) { //依據參數pluggedType和mSleepPolicy值 確定是否休眠
        if (mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER) {
            // Never sleep
            return true;
        } else if ((mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) && (pluggedType != 0)) {
            // Never sleep while plugged, and we're plugged
            return true;
        } else {
            // Default
            return shouldDeviceStayAwake(pluggedType);
       }
  }
			
    /**
     * Observes changes to wifi sleep policy
     */
    private void registerForWifiSleepPolicyChange(Handler handler) {
        ContentObserver contentObserver = new ContentObserver(handler) {
            @Override
            public void onChange(boolean selfChange) {
                readWifiSleepPolicy();
            }
        };
        mFacade.registerContentObserver(mContext,Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY), false,
                contentObserver);
    }
	
    private void readWifiSleepPolicy() {
        mSleepPolicy = mFacade.getIntegerSetting(mContext,
                Settings.Global.WIFI_SLEEP_POLICY,
                Settings.Global.WIFI_SLEEP_POLICY_NEVER);
    }
	
	
	
	
 class DefaultState extends State {
        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_SCREEN_OFF:  // 滅屏情況下
                    mScreenOff = true;

                    if (!shouldWifiStayAwake(mPluggedType)) {
//private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000  // 15分鐘
//   private static final String ACTION_DEVICE_IDLE ="com.android.server.WifiManager.action.DEVICE_IDLE";
//public static final String WIFI_IDLE_MS = "wifi_idle_ms";
// mIdleMillis = mFacade.getLongSetting(mContext,Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS); 
// Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);  private static final int IDLE_REQUEST = 0;
// private PendingIntentmIdleIntent = mFacade.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
// 【如果不保持連接,那麼設置mIdleMillis 15分鐘之後發送 ACTION_DEVICE_IDLE的廣播】
                        if (mNetworkInfo.getDetailedState() ==NetworkInfo.DetailedState.CONNECTED) {
                            if (DBG) Slog.d(TAG, "set idle timer: " + mIdleMillis + " ms");
                            mAlarmManager.set(AlarmManager.RTC_WAKEUP,System.currentTimeMillis() + mIdleMillis, mIdleIntent);
                        } else {
						//需要使機器不保持連接 當前連接狀態不爲連接那麼就 直接使發送CMD_DEVICE_IDLE消息
                            sendMessage(CMD_DEVICE_IDLE);
                        }
                    }
                    break;

                case CMD_BATTERY_CHANGED:
                    int pluggedType = msg.arg1;
                    // 如果當前滅屏  
                    if (mScreenOff && shouldWifiStayAwake(mPluggedType)【CMD_BATTERY_CHANGED消息前記錄的PluggedType】 
					&&!shouldWifiStayAwake(pluggedType) 【CMD_BATTERY_CHANGED攜帶的PluggedType】 ) {
					 // 以上條件滿足 那麼就發送 mIdleIntent 廣播
                        long triggerTime = System.currentTimeMillis() + mIdleMillis;
                        if (DBG) Slog.d(TAG, "set idle timer for " + mIdleMillis + "ms");
                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                    }

                    mPluggedType = pluggedType;
                    break;
					
					
					case CMD_DEVICE_IDLE:
                    mDeviceIdle = true;
                    updateBatteryWorkSource();
                    break;
		}
		
		}
		
		
	private final WorkSource mTmpWorkSource = new WorkSource();
	private final WifiLockManager mWifiLockManager;
	private void updateBatteryWorkSource() {
        mTmpWorkSource.clear();
        if (mDeviceIdle) {
            mTmpWorkSource.add(mWifiLockManager.createMergedWorkSource());
        }
        mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
    }
	
	
		// 處理 ACTION_DEVICE_IDLE"com.android.server.WifiManager.action.DEVICE_IDLE"廣播的處理器
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_DEVICE_IDLE);
        mContext.registerReceiver(
                new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        String action = intent.getAction();
                        if (action.equals(ACTION_DEVICE_IDLE)) {
                            sendMessage(CMD_DEVICE_IDLE); // 發送消息 CMD_DEVICE_IDLE
                        }
						...
						}
				}
						
============================================================
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java


    // Wakelock held during wifi start/stop and driver load/unload
    private PowerManager.WakeLock mWakeLock;  // 保持wifi連接的鎖Lock

    public void updateBatteryWorkSource(WorkSource newSource) {
        synchronized (mRunningWifiUids) {
            try {
                if (newSource != null) {
                    mRunningWifiUids.set(newSource);
                }
                if (mIsRunning) {
                    if (mReportedRunning) {
                        // If the work source has changed since last time, need
                        // to remove old work from battery stats.
                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,mRunningWifiUids);
                            mLastRunningWifiUids.set(mRunningWifiUids);
                        }
                    } else {
                        // Now being started, report it.
                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
                        mLastRunningWifiUids.set(mRunningWifiUids);
                        mReportedRunning = true;
                    }
                } else {
                    if (mReportedRunning) {
                        // Last reported we were running, time to stop.
                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
                        mLastRunningWifiUids.clear();
                        mReportedRunning = false;
                    }
                }
                mWakeLock.setWorkSource(newSource);
            } catch (RemoteException ignore) {
            }
        }
    }
	
	
================================================================
/frameworks/base/core/java/android/os/PowerManager.java


final IPowerManager mService; 
    public final class WakeLock {
        private WorkSource mWorkSource;
        public void setWorkSource(WorkSource ws) {
            synchronized (mToken) {
                if (ws != null && ws.size() == 0) {
                    ws = null;
                }

                final boolean changed;
                if (ws == null) {
                    changed = mWorkSource != null;
                    mWorkSource = null;
                } else if (mWorkSource == null) {
                    changed = true;
                    mWorkSource = new WorkSource(ws);
                } else {
                    changed = mWorkSource.diff(ws);
                    if (changed) {
                        mWorkSource.set(ws);
                    }
                }

                if (changed && mHeld) {
                    try {
                        mService.updateWakeLockWorkSource(mToken, mWorkSource, mHistoryTag);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
        }
================================================================
/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

        @Override // Binder call
        public void updateWakeLockWorkSource(IBinder lock, WorkSource ws, String historyTag) {
            if (lock == null) {
                throw new IllegalArgumentException("lock must not be null");
            }

            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
            if (ws != null && ws.size() != 0) {
                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.UPDATE_DEVICE_STATS, null);
            } else {
                ws = null;
            }

            final int callingUid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                updateWakeLockWorkSourceInternal(lock, ws, historyTag, callingUid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

		


    private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws, String historyTag,int callingUid) {
        synchronized (mLock) {
            int index = findWakeLockIndexLocked(lock);
            if (index < 0) {
                throw new IllegalArgumentException("Wake lock not active: " + lock+ " from uid " + callingUid);
            }
            WakeLock wakeLock = mWakeLocks.get(index);  // 獲得對應的鎖
            if (DEBUG_SPEW) {
                Slog.d(TAG, "updateWakeLockWorkSourceInternal: lock=" + Objects.hashCode(lock)+ " [" + wakeLock.mTag + "], ws=" + ws);
            }

            if (!wakeLock.hasSameWorkSource(ws)) {
                notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag,
                        wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,ws, historyTag);
                wakeLock.mHistoryTag = historyTag;
                wakeLock.updateWorkSource(ws);
            }
        }
    }
	
	
    private void notifyWakeLockChangingLocked(WakeLock wakeLock, int flags, String tag,
            String packageName, int uid, int pid, WorkSource ws, String historyTag) {
        if (mSystemReady && wakeLock.mNotifiedAcquired) {
            mNotifier.onWakeLockChanging(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
                    wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
                    wakeLock.mHistoryTag, flags, tag, packageName, uid, pid, ws, historyTag);
            notifyWakeLockLongFinishedLocked(wakeLock);
            restartNofifyLongTimerLocked(wakeLock);
        }
    }
	
    private void notifyWakeLockLongFinishedLocked(WakeLock wakeLock) { // 當前鎖完成使命
        if (wakeLock.mNotifiedLong) {
            wakeLock.mNotifiedLong = false;
            mNotifier.onLongPartialWakeLockFinish(wakeLock.mTag, wakeLock.mOwnerUid,
			wakeLock.mWorkSource, wakeLock.mHistoryTag);
        }
    }
================================================================
/frameworks/base/services/core/java/com/android/server/power/Notifier.java


    public void onLongPartialWakeLockFinish(String tag, int ownerUid, WorkSource workSource,String historyTag) {
        try {
            if (workSource != null) {
                final int N = workSource.size();
                for (int i=0; i<N; i++) { // 通知一個Wakelock完成了使命
                    mBatteryStats.noteLongPartialWakelockFinish(tag, historyTag, workSource.get(i));
                }
            } else {
                mBatteryStats.noteLongPartialWakelockFinish(tag, historyTag, ownerUid);
            }
        } catch (RemoteException ex) {
            // Ignore
        }
    }
	
================================================================
/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
 final BatteryStatsImpl mStats;
	    public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
        enforceCallingPermission();
        synchronized (mStats) {
            mStats.noteLongPartialWakelockFinish(name, historyName, uid);
        }
    }
	
================================================================
/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java

// Event for reporting that a specific partial wake lock has been held for a long duration.
public static final int EVENT_LONG_WAKE_LOCK = 0x0014;
public static final int EVENT_FLAG_FINISH = 0x4000;
        public static final int EVENT_LONG_WAKE_LOCK_FINISH =EVENT_LONG_WAKE_LOCK | EVENT_FLAG_FINISH;
				
final HistoryEventTracker mActiveEvents = new HistoryEventTracker();

    public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
        uid = mapUid(uid);
        final long elapsedRealtime = mClocks.elapsedRealtime();
        final long uptime = mClocks.uptimeMillis();
        if (historyName == null) {
            historyName = name;
        }
        if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, historyName, uid,0)) {
            return;
        }
        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,historyName, uid);
    }
	
	
	
    public final static class HistoryEventTracker {
	// 用於保存需要鎖的那些線程id ,不需要鎖了  就從map中刪除
        private final HashMap<String, SparseIntArray>[] mActiveEvents = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
				
        public boolean updateState(int code, String name, int uid, int poolIdx) {
            if ((code&HistoryItem.EVENT_FLAG_START) != 0) { //起始標識符
                int idx = code&HistoryItem.EVENT_TYPE_MASK;
                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
                if (active == null) {
                    active = new HashMap<>();
                    mActiveEvents[idx] = active;
                }
                SparseIntArray uids = active.get(name);
                if (uids == null) {
                    uids = new SparseIntArray();
                    active.put(name, uids);
                }
                if (uids.indexOfKey(uid) >= 0) {
                    // Already set, nothing to do!
                    return false;
                }
                uids.put(uid, poolIdx);
            } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) { //關閉標識符
                int idx = code&HistoryItem.EVENT_TYPE_MASK;
                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
                if (active == null) {
                    // not currently active, nothing to do.
                    return false;
                }
                SparseIntArray uids = active.get(name);
                if (uids == null) {
                    // not currently active, nothing to do.
                    return false;
                }
                idx = uids.indexOfKey(uid);
                if (idx < 0) {
                    // not currently active, nothing to do.
                    return false;
                }
                uids.removeAt(idx);  // 從Map中移除對應的進程線程id 
                if (uids.size() <= 0) {
                    active.remove(name);
                }
            }
            return true;
        }
		
final HistoryItem mHistoryCur = new HistoryItem();
    public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,String name, int uid) {
        mHistoryCur.eventCode = code;//【 HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH 】
        mHistoryCur.eventTag = mHistoryCur.localEventTag;
        mHistoryCur.eventTag.string = name;
        mHistoryCur.eventTag.uid = uid;
        addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
    }
	
	
	
	    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
        if (mTrackRunningHistoryElapsedRealtime != 0) {
            final long diffElapsed = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtime;
            final long diffUptime = uptimeMs - mTrackRunningHistoryUptime;
            if (diffUptime < (diffElapsed-20)) {
                final long wakeElapsedTime = elapsedRealtimeMs - (diffElapsed - diffUptime);
                mHistoryAddTmp.setTo(mHistoryLastWritten);
                mHistoryAddTmp.wakelockTag = null;
                mHistoryAddTmp.wakeReasonTag = null;
                mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
                mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
                addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
            }
        }
        mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
        mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
        mTrackRunningHistoryUptime = uptimeMs;
        addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
    }
	
	//【 HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH 】
	    void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
    private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
            HistoryItem cur) {
        if (mIteratingHistory) {
            throw new IllegalStateException("Can't do this while iterating history!");
        }
        mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
        mHistoryLastLastWritten.setTo(mHistoryLastWritten);
        mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
        mHistoryLastWritten.states &= mActiveHistoryStates;
        mHistoryLastWritten.states2 &= mActiveHistoryStates2;
        writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);//【填充Parcel數據】
        mLastHistoryElapsedRealtime = elapsedRealtimeMs;
        cur.wakelockTag = null;
        cur.wakeReasonTag = null;
        cur.eventCode = HistoryItem.EVENT_NONE;
        cur.eventTag = null;
        if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
                + " now " + mHistoryBuffer.dataPosition()
                + " size is now " + mHistoryBuffer.dataSize());
    }
		}
		

		
	public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
        if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
            int wakeLockIndex;
            int wakeReasonIndex;
            if (cur.wakelockTag != null) {
                wakeLockIndex = writeHistoryTag(cur.wakelockTag);
				 //DELTA: wakelock  刪除wakelock 
                if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
            } else {
                wakeLockIndex = 0xffff;
            }
            if (cur.wakeReasonTag != null) {
                wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
                if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
                    + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
            } else {
                wakeReasonIndex = 0xffff;
            }
            dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex); // 把要刪除鎖的索引寫到Parcel中
        }
		
}




    public final static class HistoryTag { // 每一個鎖 都有一個 HistoryTag
        public String string;
        public int uid;
        public int poolIdx;

        public void setTo(HistoryTag o) {
            string = o.string;
            uid = o.uid;
            poolIdx = o.poolIdx;
        }

        public void setTo(String _string, int _uid) {
            string = _string;
            uid = _uid;
            poolIdx = -1;
        }

        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(string);
            dest.writeInt(uid);
        }
		}
		

/frameworks/base/core/java/android/os/Environment.java
private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
public static File getDataDirectory() {
return DIR_ANDROID_DATA;
}
	
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system"); 【/data/system/ 】
systemDir.mkdirs();
mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
		
		
 /frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  BatteryStatsService(File systemDir 【/data/system/ 】, Handler handler)
  final BatteryStatsImpl mStats = new BatteryStatsImpl(systemDir, handler, mHandler, this);
 
 /frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
 //  感覺像把 HistoryEventTracker 裏面的  HashMap<String, SparseIntArray>[] mActiveEvents 對應的線程uid 進行鎖定
 
 private final JournaledFile mFile;  【/data/system/batterystats.bin 】 
mFile = new JournaledFile(new File(systemDir 【/data/system/ 】, "batterystats.bin"), new File(systemDir, "batterystats.bin.tmp"));
    
	
	    void writeLocked(boolean sync) {  // 寫鎖?
        if (mFile == null) {
            Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
            return;
        }

        if (mShuttingDown) {
            return;
        }

        Parcel out = Parcel.obtain();
        writeSummaryToParcel(out, true);
        mLastWriteTime = mClocks.elapsedRealtime();

        if (mPendingWrite != null) {
            mPendingWrite.recycle();
        }
        mPendingWrite = out;

        if (sync) {
            commitPendingDataToDisk(); // 寫數據到disk?
        } else {
            BackgroundThread.getHandler().post(new Runnable() {
                @Override public void run() {
                    commitPendingDataToDisk();
                }
            });
        }
    }
	
	public void commitPendingDataToDisk() {
        final Parcel next;
        synchronized (this) {
            next = mPendingWrite;
            mPendingWrite = null;
            if (next == null) {
                return;
            }
        }

        mWriteLock.lock();
        try { // 往一個文件中寫parcel?  mFile = 【/data/system/batterystats.bin 】
            FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
            stream.write(next【 Parcel 】.marshall());
            stream.flush();
            FileUtils.sync(stream);
            stream.close();
            mFile.commit();
        } catch (IOException e) {
            Slog.w("BatteryStats", "Error writing battery statistics", e);
            mFile.rollback();
        } finally {
            next.recycle();
            mWriteLock.unlock();
        }
    }
	
總結:  難道是把每一個設備鎖都封裝成一個Parcel,這些Parcel以一定方式寫入/data/system/batterystats.bin文件中
就能使得設備不進入完全休眠狀態,當要把鎖刪除也同樣把/data/system/batterystats.bin中對應的Parcel 刪除,已達到
釋放鎖的目的? No understand !!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章