diff options
author | Eric Schwarzenbach <easchwar@google.com> | 2018-04-04 18:35:04 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-04-04 18:35:04 +0000 |
commit | 9f64c6e98f79d43699ba15e286bf7c49c4c1df07 (patch) | |
tree | 3e98949a7caa054c85e46772fbbda7d160c554dd /service | |
parent | bd0ca3759ab4cbee1ffaf772702a1b2e783a5952 (diff) | |
parent | 5aad8ece7e38a80db917d49b5245f6b8c6dca273 (diff) |
Merge changes I5fd02510,I062f0005 into pi-dev
* changes:
Change onboarding flow.
Fix WifiWake locking behavior.
Diffstat (limited to 'service')
6 files changed, 309 insertions, 79 deletions
diff --git a/service/java/com/android/server/wifi/WakeupConfigStoreData.java b/service/java/com/android/server/wifi/WakeupConfigStoreData.java index b936a4c6c..d98567766 100644 --- a/service/java/com/android/server/wifi/WakeupConfigStoreData.java +++ b/service/java/com/android/server/wifi/WakeupConfigStoreData.java @@ -39,12 +39,14 @@ public class WakeupConfigStoreData implements StoreData { private static final String XML_TAG_FEATURE_STATE_SECTION = "FeatureState"; private static final String XML_TAG_IS_ACTIVE = "IsActive"; private static final String XML_TAG_IS_ONBOARDED = "IsOnboarded"; + private static final String XML_TAG_NOTIFICATIONS_SHOWN = "NotificationsShown"; private static final String XML_TAG_NETWORK_SECTION = "Network"; private static final String XML_TAG_SSID = "SSID"; private static final String XML_TAG_SECURITY = "Security"; private final DataSource<Boolean> mIsActiveDataSource; private final DataSource<Boolean> mIsOnboardedDataSource; + private final DataSource<Integer> mNotificationsDataSource; private final DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource; private boolean mHasBeenRead = false; @@ -76,9 +78,11 @@ public class WakeupConfigStoreData implements StoreData { public WakeupConfigStoreData( DataSource<Boolean> isActiveDataSource, DataSource<Boolean> isOnboardedDataSource, + DataSource<Integer> notificationsDataSource, DataSource<Set<ScanResultMatchInfo>> networkDataSource) { mIsActiveDataSource = isActiveDataSource; mIsOnboardedDataSource = isOnboardedDataSource; + mNotificationsDataSource = notificationsDataSource; mNetworkDataSource = networkDataSource; } @@ -116,6 +120,8 @@ public class WakeupConfigStoreData implements StoreData { XmlUtil.writeNextValue(out, XML_TAG_IS_ACTIVE, mIsActiveDataSource.getData()); XmlUtil.writeNextValue(out, XML_TAG_IS_ONBOARDED, mIsOnboardedDataSource.getData()); + XmlUtil.writeNextValue(out, XML_TAG_NOTIFICATIONS_SHOWN, + mNotificationsDataSource.getData()); XmlUtil.writeNextSectionEnd(out, XML_TAG_FEATURE_STATE_SECTION); } @@ -185,6 +191,7 @@ public class WakeupConfigStoreData implements StoreData { throws IOException, XmlPullParserException { boolean isActive = false; boolean isOnboarded = false; + int notificationsShown = 0; while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { String[] valueName = new String[1]; @@ -199,6 +206,9 @@ public class WakeupConfigStoreData implements StoreData { case XML_TAG_IS_ONBOARDED: isOnboarded = (Boolean) value; break; + case XML_TAG_NOTIFICATIONS_SHOWN: + notificationsShown = (Integer) value; + break; default: throw new XmlPullParserException("Unknown value found: " + valueName[0]); } @@ -206,6 +216,7 @@ public class WakeupConfigStoreData implements StoreData { mIsActiveDataSource.setData(isActive); mIsOnboardedDataSource.setData(isOnboarded); + mNotificationsDataSource.setData(notificationsShown); } /** @@ -248,6 +259,7 @@ public class WakeupConfigStoreData implements StoreData { mNetworkDataSource.setData(Collections.emptySet()); mIsActiveDataSource.setData(false); mIsOnboardedDataSource.setData(false); + mNotificationsDataSource.setData(0); } } diff --git a/service/java/com/android/server/wifi/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java index 9743390ed..e8adbb27c 100644 --- a/service/java/com/android/server/wifi/WakeupController.java +++ b/service/java/com/android/server/wifi/WakeupController.java @@ -26,7 +26,6 @@ import android.net.wifi.WifiScanner; import android.os.Handler; import android.os.Looper; import android.provider.Settings; -import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -38,6 +37,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /** @@ -127,24 +127,30 @@ public class WakeupController { mContentObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { - mWifiWakeupEnabled = mFrameworkFacade.getIntegerSetting( - mContext, Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1; - Log.d(TAG, "WifiWake " + (mWifiWakeupEnabled ? "enabled" : "disabled")); + readWifiWakeupEnabledFromSettings(); + mWakeupOnboarding.setOnboarded(); } }; mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor( Settings.Global.WIFI_WAKEUP_ENABLED), true, mContentObserver); - mContentObserver.onChange(false /* selfChange */); + readWifiWakeupEnabledFromSettings(); // registering the store data here has the effect of reading the persisted value of the // data sources after system boot finishes mWakeupConfigStoreData = new WakeupConfigStoreData( new IsActiveDataSource(), - mWakeupOnboarding.getDataSource(), + mWakeupOnboarding.getIsOnboadedDataSource(), + mWakeupOnboarding.getNotificationsDataSource(), mWakeupLock.getDataSource()); wifiConfigStore.registerStoreData(mWakeupConfigStoreData); } + private void readWifiWakeupEnabledFromSettings() { + mWifiWakeupEnabled = mFrameworkFacade.getIntegerSetting( + mContext, Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1; + Log.d(TAG, "WifiWake " + (mWifiWakeupEnabled ? "enabled" : "disabled")); + } + private void setActive(boolean isActive) { if (mIsActive != isActive) { Log.d(TAG, "Setting active to " + isActive); @@ -175,14 +181,14 @@ public class WakeupController { if (isEnabled()) { mWakeupOnboarding.maybeShowNotification(); - Set<ScanResultMatchInfo> mostRecentSavedScanResults = getMostRecentSavedScanResults(); - + Set<ScanResultMatchInfo> savedNetworksFromLatestScan = getSavedNetworksFromLatestScan(); if (mVerboseLoggingEnabled) { - Log.d(TAG, "Saved networks in most recent scan:" + mostRecentSavedScanResults); + Log.d(TAG, "Saved networks in most recent scan:" + savedNetworksFromLatestScan); } - mWifiWakeMetrics.recordStartEvent(mostRecentSavedScanResults.size()); - mWakeupLock.initialize(mostRecentSavedScanResults); + mWifiWakeMetrics.recordStartEvent(savedNetworksFromLatestScan.size()); + mWakeupLock.setLock(savedNetworksFromLatestScan); + // TODO(b/77291248): request low latency scan here } } @@ -212,18 +218,30 @@ public class WakeupController { mWakeupLock.enableVerboseLogging(mVerboseLoggingEnabled); } - /** Returns a list of saved networks from the last full scan. */ - private Set<ScanResultMatchInfo> getMostRecentSavedScanResults() { - Set<ScanResultMatchInfo> goodSavedNetworks = getGoodSavedNetworks(); + /** Returns a filtered list of saved networks from the last full scan. */ + private Set<ScanResultMatchInfo> getSavedNetworksFromLatestScan() { + Set<ScanResult> filteredScanResults = + filterScanResults(mWifiInjector.getWifiScanner().getSingleScanResults()); + Set<ScanResultMatchInfo> goodMatchInfos = toMatchInfos(filteredScanResults); + goodMatchInfos.retainAll(getGoodSavedNetworks()); + + return goodMatchInfos; + } - List<ScanResult> scanResults = mWifiInjector.getWifiScanner().getSingleScanResults(); - Set<ScanResultMatchInfo> lastSeenNetworks = new HashSet<>(scanResults.size()); - for (ScanResult scanResult : scanResults) { - lastSeenNetworks.add(ScanResultMatchInfo.fromScanResult(scanResult)); + /** Returns a set of ScanResults with all DFS channels removed. */ + private Set<ScanResult> filterScanResults(Collection<ScanResult> scanResults) { + int[] dfsChannels = mWifiInjector.getWifiNative() + .getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY); + if (dfsChannels == null) { + dfsChannels = new int[0]; } - lastSeenNetworks.retainAll(goodSavedNetworks); - return lastSeenNetworks; + final Set<Integer> dfsChannelSet = Arrays.stream(dfsChannels).boxed() + .collect(Collectors.toSet()); + + return scanResults.stream() + .filter(scanResult -> !dfsChannelSet.contains(scanResult.frequency)) + .collect(Collectors.toSet()); } /** Returns a filtered list of saved networks from WifiConfigManager. */ @@ -245,16 +263,18 @@ public class WakeupController { } //TODO(b/69271702) implement WAN filtering - private boolean isWideAreaNetwork(WifiConfiguration wifiConfiguration) { + private static boolean isWideAreaNetwork(WifiConfiguration config) { return false; } /** * Handles incoming scan results. * - * <p>The controller updates the WakeupLock with the incoming scan results. If WakeupLock is - * empty, it evaluates scan results for a match with saved networks. If a match exists, it - * enables wifi. + * <p>The controller updates the WakeupLock with the incoming scan results. If WakeupLock is not + * yet fully initialized, it adds the current scanResults to the lock and returns. If WakeupLock + * is initialized but not empty, the controller updates the lock with the current scan. If it is + * both initialized and empty, it evaluates scan results for a match with saved networks. If a + * match exists, it enables wifi. * * <p>The feature must be enabled and the store data must be loaded in order for the controller * to handle scan results. @@ -269,41 +289,22 @@ public class WakeupController { // only count scan as handled if isEnabled mNumScansHandled++; - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Incoming scan. Total scans handled: " + mNumScansHandled); - Log.d(TAG, "ScanResults: " + scanResults); + Log.d(TAG, "Incoming scan #" + mNumScansHandled); } - // need to show notification here in case user enables Wifi Wake when Wifi is off + // need to show notification here in case user turns phone on while wifi is off mWakeupOnboarding.maybeShowNotification(); - if (!mWakeupOnboarding.isOnboarded()) { - return; - } - - // only update the wakeup lock if it's not already empty - if (!mWakeupLock.isEmpty()) { - if (mVerboseLoggingEnabled) { - Log.d(TAG, "WakeupLock not empty. Updating."); - } - Set<ScanResultMatchInfo> networks = new ArraySet<>(); - for (ScanResult scanResult : scanResults) { - networks.add(ScanResultMatchInfo.fromScanResult(scanResult)); - } - mWakeupLock.update(networks); - - // if wakeup lock is still not empty, return - if (!mWakeupLock.isEmpty()) { - return; - } + Set<ScanResult> filteredScanResults = filterScanResults(scanResults); - Log.d(TAG, "WakeupLock emptied"); - mWifiWakeMetrics.recordUnlockEvent(mNumScansHandled); + mWakeupLock.update(toMatchInfos(filteredScanResults)); + if (!mWakeupLock.isUnlocked()) { + return; } ScanResult network = - mWakeupEvaluator.findViableNetwork(scanResults, getGoodSavedNetworks()); + mWakeupEvaluator.findViableNetwork(filteredScanResults, getGoodSavedNetworks()); if (network != null) { Log.d(TAG, "Enabling wifi for network: " + network.SSID); @@ -312,6 +313,15 @@ public class WakeupController { } /** + * Converts ScanResults to ScanResultMatchInfos. + */ + private static Set<ScanResultMatchInfo> toMatchInfos(Collection<ScanResult> scanResults) { + return scanResults.stream() + .map(ScanResultMatchInfo::fromScanResult) + .collect(Collectors.toSet()); + } + + /** * Enables wifi. * * <p>This method ignores all checks and assumes that {@link WifiStateMachine} is currently diff --git a/service/java/com/android/server/wifi/WakeupLock.java b/service/java/com/android/server/wifi/WakeupLock.java index 9e617a498..8a7422f46 100644 --- a/service/java/com/android/server/wifi/WakeupLock.java +++ b/service/java/com/android/server/wifi/WakeupLock.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Log; @@ -29,7 +30,7 @@ import java.util.Map; import java.util.Set; /** - * A lock to determine whether Auto Wifi can re-enable Wifi. + * A lock to determine whether Wifi Wake can re-enable Wifi. * * <p>Wakeuplock manages a list of networks to determine whether the device's location has changed. */ @@ -39,44 +40,144 @@ public class WakeupLock { @VisibleForTesting static final int CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT = 3; - + @VisibleForTesting + static final long MAX_LOCK_TIME_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS; private final WifiConfigManager mWifiConfigManager; private final Map<ScanResultMatchInfo, Integer> mLockedNetworks = new ArrayMap<>(); + private final WifiWakeMetrics mWifiWakeMetrics; + private final Clock mClock; + private boolean mVerboseLoggingEnabled; + private long mLockTimestamp; + private boolean mIsInitialized; + private int mNumScans; - public WakeupLock(WifiConfigManager wifiConfigManager) { + public WakeupLock(WifiConfigManager wifiConfigManager, WifiWakeMetrics wifiWakeMetrics, + Clock clock) { mWifiConfigManager = wifiConfigManager; + mWifiWakeMetrics = wifiWakeMetrics; + mClock = clock; } /** - * Initializes the WakeupLock with the given {@link ScanResultMatchInfo} list. + * Sets the WakeupLock with the given {@link ScanResultMatchInfo} list. * - * <p>This saves the wakeup lock to the store. + * <p>This saves the wakeup lock to the store and begins the initialization process. * * @param scanResultList list of ScanResultMatchInfos to start the lock with */ - public void initialize(Collection<ScanResultMatchInfo> scanResultList) { + public void setLock(Collection<ScanResultMatchInfo> scanResultList) { + mLockTimestamp = mClock.getElapsedSinceBootMillis(); + mIsInitialized = false; + mNumScans = 0; + mLockedNetworks.clear(); for (ScanResultMatchInfo scanResultMatchInfo : scanResultList) { mLockedNetworks.put(scanResultMatchInfo, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT); } - Log.d(TAG, "Lock initialized. Number of networks: " + mLockedNetworks.size()); + Log.d(TAG, "Lock set. Number of networks: " + mLockedNetworks.size()); mWifiConfigManager.saveToStore(false /* forceWrite */); } /** - * Updates the lock with the given {@link ScanResultMatchInfo} list. + * Maybe sets the WakeupLock as initialized based on total scans handled. + * + * @param numScans total number of elapsed scans in the current WifiWake session + * @return Whether the lock transitioned into its initialized state. + */ + private boolean maybeSetInitializedByScans(int numScans) { + if (mIsInitialized) { + return false; + } + boolean shouldBeInitialized = numScans >= CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; + if (shouldBeInitialized) { + mIsInitialized = true; + + Log.d(TAG, "Lock initialized by handled scans. Scans: " + numScans); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "State of lock: " + mLockedNetworks); + } + } + return mIsInitialized; + } + + /** + * Maybe sets the WakeupLock as initialized based on elapsed time. + * + * @param timestampMillis current timestamp + * @return Whether the lock transitioned into its initialized state. + */ + private boolean maybeSetInitializedByTimeout(long timestampMillis) { + if (mIsInitialized) { + return false; + } + long elapsedTime = timestampMillis - mLockTimestamp; + boolean shouldBeInitialized = elapsedTime > MAX_LOCK_TIME_MILLIS; + + if (shouldBeInitialized) { + mIsInitialized = true; + + Log.d(TAG, "Lock initialized by timeout. Elapsed time: " + elapsedTime); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "State of lock: " + mLockedNetworks); + } + } + return mIsInitialized; + } + + /** Returns whether the lock has been fully initialized. */ + public boolean isInitialized() { + return mIsInitialized; + } + + /** + * Adds the given networks to the lock. + * + * <p>This is called during the initialization step. + * + * @param networkList The list of networks to be added + */ + private void addToLock(Collection<ScanResultMatchInfo> networkList) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Initializing lock with networks: " + networkList); + } + + boolean hasChanged = false; + + for (ScanResultMatchInfo network : networkList) { + if (!mLockedNetworks.containsKey(network)) { + mLockedNetworks.put(network, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT); + hasChanged = true; + } + } + + if (hasChanged) { + mWifiConfigManager.saveToStore(false /* forceWrite */); + } + + // Set initialized if the lock has handled enough scans, and log the event + if (maybeSetInitializedByScans(mNumScans)) { + // TODO(easchwar) log metric + } + } + + /** + * Removes networks from the lock if not present in the given {@link ScanResultMatchInfo} list. * * <p>If a network in the lock is not present in the list, reduce the number of scans * required to evict by one. Remove any entries in the list with 0 scans required to evict. If * any entries in the lock are removed, the store is updated. * - * @param scanResultList list of present ScanResultMatchInfos to update the lock with + * @param networkList list of present ScanResultMatchInfos to update the lock with */ - public void update(Collection<ScanResultMatchInfo> scanResultList) { + private void removeFromLock(Collection<ScanResultMatchInfo> networkList) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Filtering lock with networks: " + networkList); + } + boolean hasChanged = false; Iterator<Map.Entry<ScanResultMatchInfo, Integer>> it = mLockedNetworks.entrySet().iterator(); @@ -84,7 +185,7 @@ public class WakeupLock { Map.Entry<ScanResultMatchInfo, Integer> entry = it.next(); // if present in scan list, reset to max - if (scanResultList.contains(entry.getKey())) { + if (networkList.contains(entry.getKey())) { if (mVerboseLoggingEnabled) { Log.d(TAG, "Found network in lock: " + entry.getKey().networkSsid); } @@ -104,13 +205,50 @@ public class WakeupLock { if (hasChanged) { mWifiConfigManager.saveToStore(false /* forceWrite */); } + + if (isUnlocked()) { + Log.d(TAG, "Lock emptied. Recording unlock event."); + mWifiWakeMetrics.recordUnlockEvent(mNumScans); + } } /** - * Returns whether the internal network set is empty. + * Updates the lock with the given {@link ScanResultMatchInfo} list. + * + * <p>Based on the current initialization state of the lock, either adds or removes networks + * from the lock. + * + * <p>The lock is initialized after {@link #CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} + * scans have been handled, or after {@link #MAX_LOCK_TIME_MILLIS} milliseconds have elapsed + * since {@link #setLock(Collection, long)}. + * + * @param networkList list of present ScanResultMatchInfos to update the lock with */ - public boolean isEmpty() { - return mLockedNetworks.isEmpty(); + public void update(Collection<ScanResultMatchInfo> networkList) { + // update is no-op if already unlocked + if (isUnlocked()) { + return; + } + mNumScans++; + + // Before checking mIsInitialized, we check to see whether we've exceeded the maximum time + // allowed for initialization. If so, we set initialized and treat this scan as a + // "removeFromLock()" instead of an "addToLock()". + if (maybeSetInitializedByTimeout(mClock.getElapsedSinceBootMillis())) { + // TODO(easchwar) log metric + } + + // add or remove networks based on initialized status + if (mIsInitialized) { + removeFromLock(networkList); + } else { + addToLock(networkList); + } + } + + /** Returns whether the WakeupLock is unlocked */ + public boolean isUnlocked() { + return mIsInitialized && mLockedNetworks.isEmpty(); } /** Returns the data source for the WakeupLock config store data. */ @@ -121,6 +259,8 @@ public class WakeupLock { /** Dumps wakeup lock contents. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("WakeupLock: "); + pw.println("mNumScans: " + mNumScans); + pw.println("mIsInitialized: " + mIsInitialized); pw.println("Locked networks: " + mLockedNetworks.size()); for (Map.Entry<ScanResultMatchInfo, Integer> entry : mLockedNetworks.entrySet()) { pw.println(entry.getKey() + ", scans to evict: " + entry.getValue()); @@ -146,7 +286,8 @@ public class WakeupLock { for (ScanResultMatchInfo network : data) { mLockedNetworks.put(network, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT); } - + // lock is considered initialized if loaded from store + mIsInitialized = true; } } } diff --git a/service/java/com/android/server/wifi/WakeupNotificationFactory.java b/service/java/com/android/server/wifi/WakeupNotificationFactory.java index 42ae46707..23f31a7db 100644 --- a/service/java/com/android/server/wifi/WakeupNotificationFactory.java +++ b/service/java/com/android/server/wifi/WakeupNotificationFactory.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import com.android.internal.R; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -37,6 +38,9 @@ public class WakeupNotificationFactory { public static final String ACTION_TURN_OFF_WIFI_WAKE = "com.android.server.wifi.wakeup.TURN_OFF_WIFI_WAKE"; + /** Notification channel ID for onboarding messages. */ + public static final int ONBOARD_ID = SystemMessage.NOTE_WIFI_WAKE_ONBOARD; + private final Context mContext; private final FrameworkFacade mFrameworkFacade; diff --git a/service/java/com/android/server/wifi/WakeupOnboarding.java b/service/java/com/android/server/wifi/WakeupOnboarding.java index d4caa0fdb..b6bcbc3c0 100644 --- a/service/java/com/android/server/wifi/WakeupOnboarding.java +++ b/service/java/com/android/server/wifi/WakeupOnboarding.java @@ -27,22 +27,31 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; import android.provider.Settings; +import android.text.format.DateUtils; import android.util.Log; -import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.annotations.VisibleForTesting; /** * Manages the WiFi Wake onboarding notification. * * <p>If a user disables wifi with Wifi Wake enabled, this notification is shown to explain that - * wifi may turn back on automatically. Wifi will not automatically turn back on until after the - * user interacts with the onboarding notification in some way (e.g. dismiss, tap). + * wifi may turn back on automatically. It will be displayed up to 3 times, or until the + * user either interacts with the onboarding notification in some way (e.g. dismiss, tap) or + * manually enables/disables the feature in WifiSettings. */ public class WakeupOnboarding { private static final String TAG = "WakeupOnboarding"; + @VisibleForTesting + static final int NOTIFICATIONS_UNTIL_ONBOARDED = 3; + @VisibleForTesting + static final long REQUIRED_NOTIFICATION_DELAY = DateUtils.DAY_IN_MILLIS; + private static final long NOT_SHOWN_TIMESTAMP = -1; + private final Context mContext; private final WakeupNotificationFactory mWakeupNotificationFactory; private NotificationManager mNotificationManager; @@ -52,6 +61,8 @@ public class WakeupOnboarding { private final FrameworkFacade mFrameworkFacade; private boolean mIsOnboarded; + private int mTotalNotificationsShown; + private long mLastShownTimestamp = NOT_SHOWN_TIMESTAMP; private boolean mIsNotificationShowing; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -104,17 +115,46 @@ public class WakeupOnboarding { /** Shows the onboarding notification if applicable. */ public void maybeShowNotification() { - if (isOnboarded() || mIsNotificationShowing) { + maybeShowNotification(SystemClock.elapsedRealtime()); + } + + @VisibleForTesting + void maybeShowNotification(long timestamp) { + if (!shouldShowNotification(timestamp)) { return; } - Log.d(TAG, "Showing onboarding notification."); + incrementTotalNotificationsShown(); + mIsNotificationShowing = true; + mLastShownTimestamp = timestamp; + mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null /* broadcastPermission */, mHandler); - getNotificationManager().notify(SystemMessage.NOTE_WIFI_WAKE_ONBOARD, + getNotificationManager().notify(WakeupNotificationFactory.ONBOARD_ID, mWakeupNotificationFactory.createOnboardingNotification()); - mIsNotificationShowing = true; + } + + /** + * Increment the total number of shown notifications and onboard the user if reached the + * required amount. + */ + private void incrementTotalNotificationsShown() { + mTotalNotificationsShown++; + if (mTotalNotificationsShown >= NOTIFICATIONS_UNTIL_ONBOARDED) { + setOnboarded(); + } else { + mWifiConfigManager.saveToStore(false /* forceWrite */); + } + } + + private boolean shouldShowNotification(long timestamp) { + if (isOnboarded() || mIsNotificationShowing) { + return false; + } + + return mLastShownTimestamp == NOT_SHOWN_TIMESTAMP + || (timestamp - mLastShownTimestamp) > REQUIRED_NOTIFICATION_DELAY; } /** Handles onboarding cleanup on stop. */ @@ -132,11 +172,15 @@ public class WakeupOnboarding { } mContext.unregisterReceiver(mBroadcastReceiver); - getNotificationManager().cancel(SystemMessage.NOTE_WIFI_WAKE_ONBOARD); + getNotificationManager().cancel(WakeupNotificationFactory.ONBOARD_ID); mIsNotificationShowing = false; } - private void setOnboarded() { + /** Sets the user as onboarded and persists to store. */ + public void setOnboarded() { + if (mIsOnboarded) { + return; + } Log.d(TAG, "Setting user as onboarded."); mIsOnboarded = true; mWifiConfigManager.saveToStore(false /* forceWrite */); @@ -150,12 +194,17 @@ public class WakeupOnboarding { return mNotificationManager; } - /** Returns the {@link WakeupConfigStoreData.DataSource} for the {@link WifiConfigStore}. */ - public WakeupConfigStoreData.DataSource<Boolean> getDataSource() { - return new OnboardingDataSource(); + /** Returns the {@link WakeupConfigStoreData.DataSource} for the onboarded status. */ + public WakeupConfigStoreData.DataSource<Boolean> getIsOnboadedDataSource() { + return new IsOnboardedDataSource(); } - private class OnboardingDataSource implements WakeupConfigStoreData.DataSource<Boolean> { + /** Returns the {@link WakeupConfigStoreData.DataSource} for the notification status. */ + public WakeupConfigStoreData.DataSource<Integer> getNotificationsDataSource() { + return new NotificationsDataSource(); + } + + private class IsOnboardedDataSource implements WakeupConfigStoreData.DataSource<Boolean> { @Override public Boolean getData() { @@ -167,4 +216,17 @@ public class WakeupOnboarding { mIsOnboarded = data; } } + + private class NotificationsDataSource implements WakeupConfigStoreData.DataSource<Integer> { + + @Override + public Integer getData() { + return mTotalNotificationsShown; + } + + @Override + public void setData(Integer data) { + mTotalNotificationsShown = data; + } + } } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 04bb3f48c..9b2df953c 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -273,7 +273,8 @@ public class WifiInjector { mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, wakeupNotificationFactory); mWakeupController = new WakeupController(mContext, - mWifiStateMachineHandlerThread.getLooper(), new WakeupLock(mWifiConfigManager), + mWifiStateMachineHandlerThread.getLooper(), + new WakeupLock(mWifiConfigManager, mWifiMetrics.getWakeupMetrics(), mClock), WakeupEvaluator.fromContext(mContext), wakeupOnboarding, mWifiConfigManager, mWifiConfigStore, mWifiMetrics.getWakeupMetrics(), this, mFrameworkFacade); mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService()); |