1 /* 2 * Copyright (C) 2022 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.settingslib.wifi; 18 19 import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE; 20 import static android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION; 21 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; 22 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; 23 24 import android.annotation.AnyThread; 25 import android.annotation.TestApi; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.net.wifi.WifiManager; 31 import android.os.HandlerThread; 32 import android.os.Process; 33 import android.util.Log; 34 35 import androidx.annotation.GuardedBy; 36 import androidx.annotation.NonNull; 37 import androidx.annotation.VisibleForTesting; 38 import androidx.annotation.WorkerThread; 39 40 import java.util.Map; 41 import java.util.concurrent.ConcurrentHashMap; 42 43 /** 44 * This is a singleton class for Wi-Fi state worker. 45 */ 46 public class WifiStateWorker { 47 48 private static final String TAG = "WifiStateWorker"; 49 private static final Object sLock = new Object(); 50 51 /** 52 * A singleton {@link WifiStateWorker} object is used to share with all sub-settings. 53 */ 54 @GuardedBy("sLock") 55 private static WifiStateWorker sInstance; 56 @TestApi 57 @GuardedBy("sLock") 58 private static Map<Context, WifiStateWorker> sTestInstances; 59 60 @VisibleForTesting 61 static WifiManager sWifiManager; 62 private static int sWifiState; 63 private static HandlerThread sWorkerThread; 64 65 /** 66 * Static method to create a singleton class for WifiStateWorker. 67 * 68 * @param context The Context this is associated with. 69 * @return an instance of {@link WifiStateWorker} object. 70 */ 71 @NonNull 72 @AnyThread getInstance(@onNull Context context)73 public static WifiStateWorker getInstance(@NonNull Context context) { 74 synchronized (sLock) { 75 if (sTestInstances != null && sTestInstances.containsKey(context)) { 76 WifiStateWorker testInstance = sTestInstances.get(context); 77 Log.w(TAG, "The context owner try to use a test instance:" + testInstance); 78 return testInstance; 79 } 80 81 if (sInstance != null) return sInstance; 82 83 sInstance = new WifiStateWorker(); 84 sWorkerThread = new HandlerThread( 85 TAG + ":{" + context.getApplicationInfo().className + "}", 86 Process.THREAD_PRIORITY_DISPLAY); 87 sWorkerThread.start(); 88 sWorkerThread.getThreadHandler().post(() -> init(context)); 89 return sInstance; 90 } 91 } 92 93 /** 94 * A convenience method to set pre-prepared instance or mock(WifiStateWorker.class) for testing. 95 * 96 * @param context The Context this is associated with. 97 * @param instance of {@link WifiStateWorker} object. 98 * @hide 99 */ 100 @TestApi 101 @VisibleForTesting setTestInstance(@onNull Context context, WifiStateWorker instance)102 public static void setTestInstance(@NonNull Context context, WifiStateWorker instance) { 103 synchronized (sLock) { 104 if (sTestInstances == null) sTestInstances = new ConcurrentHashMap<>(); 105 106 Log.w(TAG, "Try to set a test instance by context:" + context); 107 sTestInstances.put(context, instance); 108 } 109 } 110 111 @WorkerThread init(@onNull Context context)112 private static void init(@NonNull Context context) { 113 final Context appContext = context.getApplicationContext(); 114 final IntentReceiver receiver = new IntentReceiver(); 115 appContext.registerReceiver(receiver, new IntentFilter(WIFI_STATE_CHANGED_ACTION), null, 116 sWorkerThread.getThreadHandler()); 117 sWifiManager = appContext.getSystemService(WifiManager.class); 118 refresh(); 119 } 120 121 /** 122 * Refresh Wi-Fi state with WifiManager#getWifiState() 123 */ 124 @AnyThread refresh()125 public static void refresh() { 126 if (sWifiManager == null) return; 127 Log.d(TAG, "Start calling WifiManager#getWifiState."); 128 sWifiState = sWifiManager.getWifiState(); 129 Log.d(TAG, "WifiManager#getWifiState return state:" + sWifiState); 130 } 131 132 /** 133 * Gets the Wi-Fi enabled state. 134 * 135 * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, 136 * {@link WifiManager#WIFI_STATE_DISABLING}, {@link WifiManager#WIFI_STATE_ENABLED}, 137 * {@link WifiManager#WIFI_STATE_ENABLING}, {@link WifiManager#WIFI_STATE_UNKNOWN} 138 * @see #isWifiEnabled() 139 */ 140 @AnyThread getWifiState()141 public int getWifiState() { 142 return sWifiState; 143 } 144 145 /** 146 * Return whether Wi-Fi is enabled or disabled. 147 * 148 * @return {@code true} if Wi-Fi is enabled 149 * @see #getWifiState() 150 */ 151 @AnyThread isWifiEnabled()152 public boolean isWifiEnabled() { 153 return sWifiState == WIFI_STATE_ENABLED; 154 } 155 156 @WorkerThread 157 private static class IntentReceiver extends BroadcastReceiver { 158 @Override onReceive(Context context, Intent intent)159 public void onReceive(Context context, Intent intent) { 160 if (WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) { 161 sWifiState = intent.getIntExtra(EXTRA_WIFI_STATE, WIFI_STATE_DISABLED); 162 } 163 } 164 } 165 } 166