1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import android.net.wifi.WifiConfiguration;
20 import android.telephony.SubscriptionManager;
21 import android.util.ArraySet;
22 
23 import com.android.server.wifi.util.MissingCounterTimerLockList;
24 
25 import java.io.FileDescriptor;
26 import java.io.PrintWriter;
27 import java.util.Set;
28 
29 /**
30  * Keep track of the disabled duration for all non-carrier-merged networks.
31  */
32 public class NonCarrierMergedNetworksStatusTracker {
33     private final Clock mClock;
34     private int mSubscriptionId;
35     private long mDisableStartTimeMs;
36     private long mMinDisableDurationMs;
37     private long mMaxDisableDurationMs;
38     private final MissingCounterTimerLockList<String> mTemporarilyDisabledNonCarrierMergedList;
39     private final Set<String> mTemporarilyDisabledNonCarrierMergedListAtStart;
40 
NonCarrierMergedNetworksStatusTracker(Clock clock)41     public NonCarrierMergedNetworksStatusTracker(Clock clock) {
42         mClock = clock;
43         mTemporarilyDisabledNonCarrierMergedList =
44                 new MissingCounterTimerLockList<>(
45                         WifiConfigManager.SCAN_RESULT_MISSING_COUNT_THRESHOLD, mClock);
46         mTemporarilyDisabledNonCarrierMergedListAtStart = new ArraySet<>();
47         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
48     }
49 
50     /**
51      * Disable autojoin for all non-carrier-merged networks for the specified duration.
52      * @param subscriptionId the subscription ID of the carrier network.
53      * @param minDisableDurationMs the minimum duration in milliseconds to stay on the carrier
54      *                             network even if wifi is available.
55      * @param maxDisableDurationMs the maximum duration to disable all non-carrier merged wifi
56      *                             networks. After this duration passes, all non-carrier merged
57      *                             networks will get re-enabled for auto connections.
58      */
disableAllNonCarrierMergedNetworks(int subscriptionId, long minDisableDurationMs, long maxDisableDurationMs)59     public void disableAllNonCarrierMergedNetworks(int subscriptionId, long minDisableDurationMs,
60             long maxDisableDurationMs) {
61         mSubscriptionId = subscriptionId;
62         mDisableStartTimeMs = mClock.getElapsedSinceBootMillis();
63         mMinDisableDurationMs = minDisableDurationMs;
64         mMaxDisableDurationMs = maxDisableDurationMs;
65     }
66 
67     /**
68      * Add a SSID or FQDN to the temporary disabled list for the given timer duration. The SSID
69      * or FQDN will be re-enabled when after it is out of range for the specified duration.
70      */
temporarilyDisableNetwork(WifiConfiguration config, long timerDurationMs, long maxDisableDurationMs)71     public void temporarilyDisableNetwork(WifiConfiguration config, long timerDurationMs,
72             long maxDisableDurationMs) {
73         String key = getKeyFromConfig(config);
74         mTemporarilyDisabledNonCarrierMergedList.add(key, timerDurationMs, maxDisableDurationMs);
75         mTemporarilyDisabledNonCarrierMergedListAtStart.add(key);
76     }
77 
78     /**
79      * Used to detect whether a disabled network is still in range.
80      * A disabled network that does not show up in the list passed in here for |timerDurationMs|
81      * will be re-enabled.
82      */
update(Set<String> networks)83     public void update(Set<String> networks) {
84         mTemporarilyDisabledNonCarrierMergedList.update(networks);
85     }
86 
87     /**
88      * Resets this class and re-enables auto-join for all non-carrier-merged networks.
89      */
clear()90     public void clear() {
91         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
92         mDisableStartTimeMs = 0;
93         mMinDisableDurationMs = 0;
94         mMaxDisableDurationMs = 0;
95         mTemporarilyDisabledNonCarrierMergedList.clear();
96         mTemporarilyDisabledNonCarrierMergedListAtStart.clear();
97     }
98 
99     /**
100      * Returns whether the given network should be not allowed for auto-connect.
101      * A network could be disable either because all non-carrier-merged networks are not allowed,
102      * or this specific network is still in the temporarily disabled list.
103      * @param config the network to check whether auto-connect should be disabled on
104      */
isNetworkDisabled(WifiConfiguration config)105     public boolean isNetworkDisabled(WifiConfiguration config) {
106         // All wifi networks are enabled after the max effective duration of the API call passes.
107         if (mClock.getElapsedSinceBootMillis() - mDisableStartTimeMs >= mMaxDisableDurationMs) {
108             clear();
109             return false;
110         }
111         // always allow a carrier-merged network with matching subscription ID through.
112         if (config.carrierMerged && config.subscriptionId == mSubscriptionId) {
113             return false;
114         }
115         if (shouldDisableAllNonCarrierMergedNetworks()) {
116             return true;
117         }
118         String key = getKeyFromConfig(config);
119         if (mTemporarilyDisabledNonCarrierMergedList.isLocked(key)) {
120             return true;
121         }
122         mTemporarilyDisabledNonCarrierMergedList.remove(key);
123         return false;
124     }
125 
getKeyFromConfig(WifiConfiguration config)126     private String getKeyFromConfig(WifiConfiguration config) {
127         return config.isPasspoint() ? config.FQDN : config.SSID;
128     }
129 
shouldDisableAllNonCarrierMergedNetworks()130     private boolean shouldDisableAllNonCarrierMergedNetworks() {
131         return mClock.getElapsedSinceBootMillis() - mDisableStartTimeMs < mMinDisableDurationMs;
132     }
133 
134     /**
135      * Dump information for debugging.
136      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)137     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
138         pw.println("NonCarrierMergedNetworksStatusTracker - Log Begin ----");
139         pw.println("mSubscriptionId=" + mSubscriptionId);
140         pw.println("dumpTimeMs=" + mClock.getElapsedSinceBootMillis());
141         pw.println("mDisableStartTimeMs=" + mDisableStartTimeMs);
142         pw.println("mMinDisableDurationMs=" + mMinDisableDurationMs);
143         pw.println("mMaxDisableDurationMs=" + mMaxDisableDurationMs);
144         pw.println("mTemporarilyDisabledNonCarrierMergedListAtStart=");
145         for (String s : mTemporarilyDisabledNonCarrierMergedListAtStart) {
146             pw.println(s);
147         }
148         pw.println("NonCarrierMergedNetworksStatusTracker - Log End ----");
149     }
150 }
151