1 /*
2  * Copyright (C) 2018 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.settings.wifi;
18 
19 import android.content.Context;
20 import android.net.ConnectivityManager;
21 import android.net.NetworkScoreManager;
22 import android.net.wifi.WifiManager;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.Looper;
27 import android.os.Process;
28 import android.os.SimpleClock;
29 import android.os.SystemClock;
30 
31 import androidx.annotation.VisibleForTesting;
32 import androidx.preference.PreferenceGroup;
33 import androidx.preference.PreferenceScreen;
34 
35 import com.android.settings.R;
36 import com.android.settings.core.SubSettingLauncher;
37 import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
38 import com.android.settingslib.core.AbstractPreferenceController;
39 import com.android.settingslib.core.lifecycle.Lifecycle;
40 import com.android.settingslib.wifi.WifiEntryPreference;
41 import com.android.wifitrackerlib.WifiEntry;
42 import com.android.wifitrackerlib.WifiPickerTracker;
43 
44 import java.time.Clock;
45 import java.time.ZoneOffset;
46 
47 // TODO(b/151133650): Replace AbstractPreferenceController with BasePreferenceController.
48 /**
49  * This places a preference into a PreferenceGroup owned by some parent
50  * controller class when there is a wifi connection present.
51  */
52 public class WifiConnectionPreferenceController extends AbstractPreferenceController implements
53         WifiPickerTracker.WifiPickerTrackerCallback {
54 
55     private static final String TAG = "WifiConnPrefCtrl";
56 
57     private static final String KEY = "active_wifi_connection";
58 
59     // Max age of tracked WifiEntries.
60     private static final long MAX_SCAN_AGE_MILLIS = 15_000;
61     // Interval between initiating WifiPickerTracker scans.
62     private static final long SCAN_INTERVAL_MILLIS = 10_000;
63 
64     private UpdateListener mUpdateListener;
65     private Context mPrefContext;
66     private String mPreferenceGroupKey;
67     private PreferenceGroup mPreferenceGroup;
68     @VisibleForTesting
69     public WifiPickerTracker mWifiPickerTracker;
70     private WifiEntryPreference mPreference;
71     private int order;
72     private int mMetricsCategory;
73     // Worker thread used for WifiPickerTracker work.
74     private HandlerThread mWorkerThread;
75 
76     /**
77      * Used to notify a parent controller that this controller has changed in availability, or has
78      * updated the content in the preference that it manages.
79      */
80     public interface UpdateListener {
onChildrenUpdated()81         void onChildrenUpdated();
82     }
83 
84     /**
85      * @param context            the context for the UI where we're placing the preference
86      * @param lifecycle          for listening to lifecycle events for the UI
87      * @param updateListener     for notifying a parent controller of changes
88      * @param preferenceGroupKey the key to use to lookup the PreferenceGroup where this controller
89      *                           will add its preference
90      * @param order              the order that the preference added by this controller should use -
91      *                           useful when this preference needs to be ordered in a specific way
92      *                           relative to others in the PreferenceGroup
93      * @param metricsCategory    - the category to use as the source when handling the click on the
94      *                           pref to go to the wifi connection detail page
95      */
WifiConnectionPreferenceController(Context context, Lifecycle lifecycle, UpdateListener updateListener, String preferenceGroupKey, int order, int metricsCategory)96     public WifiConnectionPreferenceController(Context context, Lifecycle lifecycle,
97             UpdateListener updateListener, String preferenceGroupKey, int order,
98             int metricsCategory) {
99         super(context);
100         mUpdateListener = updateListener;
101         mPreferenceGroupKey = preferenceGroupKey;
102         this.order = order;
103         mMetricsCategory = metricsCategory;
104 
105         mWorkerThread = new HandlerThread(
106                 TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
107                 Process.THREAD_PRIORITY_BACKGROUND);
108         mWorkerThread.start();
109         final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
110             @Override
111             public long millis() {
112                 return SystemClock.elapsedRealtime();
113             }
114         };
115         mWifiPickerTracker = new WifiPickerTracker(lifecycle, context,
116                 context.getSystemService(WifiManager.class),
117                 context.getSystemService(ConnectivityManager.class),
118                 context.getSystemService(NetworkScoreManager.class),
119                 new Handler(Looper.getMainLooper()),
120                 mWorkerThread.getThreadHandler(),
121                 elapsedRealtimeClock,
122                 MAX_SCAN_AGE_MILLIS,
123                 SCAN_INTERVAL_MILLIS,
124                 this);
125     }
126 
127     @Override
isAvailable()128     public boolean isAvailable() {
129         return mWifiPickerTracker.getConnectedWifiEntry() != null;
130     }
131 
132     @Override
getPreferenceKey()133     public String getPreferenceKey() {
134         return KEY;
135     }
136 
137     @Override
displayPreference(PreferenceScreen screen)138     public void displayPreference(PreferenceScreen screen) {
139         super.displayPreference(screen);
140         mPreferenceGroup = screen.findPreference(mPreferenceGroupKey);
141         mPrefContext = screen.getContext();
142         update();
143     }
144 
updatePreference(WifiEntry wifiEntry)145     private void updatePreference(WifiEntry wifiEntry) {
146         if (mPreference != null) {
147             mPreferenceGroup.removePreference(mPreference);
148             mPreference = null;
149         }
150         if (wifiEntry == null || mPrefContext == null) {
151             return;
152         }
153 
154         mPreference = new WifiEntryPreference(mPrefContext, wifiEntry);
155         mPreference.setKey(KEY);
156         mPreference.refresh();
157         mPreference.setOrder(order);
158         mPreference.setOnPreferenceClickListener(pref -> {
159             final Bundle args = new Bundle();
160             args.putString(WifiNetworkDetailsFragment2.KEY_CHOSEN_WIFIENTRY_KEY,
161                     wifiEntry.getKey());
162             new SubSettingLauncher(mPrefContext)
163                     .setTitleRes(R.string.pref_title_network_details)
164                     .setDestination(WifiNetworkDetailsFragment2.class.getName())
165                     .setArguments(args)
166                     .setSourceMetricsCategory(mMetricsCategory)
167                     .launch();
168             return true;
169         });
170         mPreferenceGroup.addPreference(mPreference);
171     }
172 
update()173     private void update() {
174         final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();
175         if (connectedWifiEntry == null) {
176             updatePreference(null);
177         } else {
178             if (mPreference == null || !mPreference.getWifiEntry().equals(connectedWifiEntry)) {
179                 updatePreference(connectedWifiEntry);
180             } else if (mPreference != null) {
181                 mPreference.refresh();
182             }
183         }
184         mUpdateListener.onChildrenUpdated();
185     }
186 
187     /** Called when the state of Wifi has changed. */
188     @Override
onWifiStateChanged()189     public void onWifiStateChanged() {
190         update();
191     }
192 
193     /**
194      * Update the results when data changes.
195      */
196     @Override
onWifiEntriesChanged()197     public void onWifiEntriesChanged() {
198         update();
199     }
200 
201     @Override
onNumSavedSubscriptionsChanged()202     public void onNumSavedSubscriptionsChanged() {
203         // Do nothing.
204     }
205 
206     @Override
onNumSavedNetworksChanged()207     public void onNumSavedNetworksChanged() {
208         // Do nothing.
209     }
210 }
211