1 /* 2 * Copyright (C) 2016 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 static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 20 21 import android.app.Activity; 22 import android.app.ActivityManager; 23 import android.app.IActivityManager; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageItemInfo; 31 import android.content.pm.PackageManager; 32 import android.net.wifi.WifiManager; 33 import android.os.Bundle; 34 import android.os.RemoteException; 35 import android.text.TextUtils; 36 import android.util.Log; 37 38 import androidx.annotation.NonNull; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.app.AlertActivity; 42 import com.android.settings.R; 43 44 /** 45 * This activity handles requests to toggle WiFi by collecting user 46 * consent and waiting until the state change is completed. 47 */ 48 public class RequestToggleWiFiActivity extends AlertActivity 49 implements DialogInterface.OnClickListener { 50 private static final String LOG_TAG = "RequestToggleWiFiActivity"; 51 52 private static final long TOGGLE_TIMEOUT_MILLIS = 10000; // 10 sec 53 54 private static final int STATE_UNKNOWN = -1; 55 private static final int STATE_ENABLE = 1; 56 private static final int STATE_ENABLING = 2; 57 private static final int STATE_DISABLE = 3; 58 private static final int STATE_DISABLING = 4; 59 60 private final StateChangeReceiver mReceiver = new StateChangeReceiver(); 61 62 private final Runnable mTimeoutCommand = () -> { 63 if (!isFinishing() && !isDestroyed()) { 64 finish(); 65 } 66 }; 67 68 private @NonNull WifiManager mWiFiManager; 69 private @NonNull CharSequence mAppLabel; 70 @VisibleForTesting 71 protected IActivityManager mActivityManager = ActivityManager.getService(); 72 73 private int mState = STATE_UNKNOWN; 74 private int mLastUpdateState = STATE_UNKNOWN; 75 76 @Override onCreate(Bundle savedInstanceState)77 protected void onCreate(Bundle savedInstanceState) { 78 super.onCreate(savedInstanceState); 79 getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 80 mWiFiManager = getSystemService(WifiManager.class); 81 82 setResult(Activity.RESULT_CANCELED); 83 84 mAppLabel = getAppLabel(); 85 if (TextUtils.isEmpty(mAppLabel)) { 86 finish(); 87 return; 88 } 89 90 String action = getIntent().getAction(); 91 switch (action) { 92 case WifiManager.ACTION_REQUEST_ENABLE: { 93 mState = STATE_ENABLE; 94 } break; 95 96 case WifiManager.ACTION_REQUEST_DISABLE: { 97 mState = STATE_DISABLE; 98 } break; 99 100 default: { 101 finish(); 102 } 103 } 104 } 105 106 @Override onClick(DialogInterface dialog, int which)107 public void onClick(DialogInterface dialog, int which) { 108 switch (which) { 109 case DialogInterface.BUTTON_POSITIVE: { 110 switch (mState) { 111 case STATE_ENABLE: { 112 mWiFiManager.setWifiEnabled(true); 113 mState = STATE_ENABLING; 114 scheduleToggleTimeout(); 115 updateUi(); 116 } break; 117 118 case STATE_DISABLE: { 119 mWiFiManager.setWifiEnabled(false); 120 mState = STATE_DISABLING; 121 scheduleToggleTimeout(); 122 updateUi(); 123 } break; 124 } 125 } 126 break; 127 case DialogInterface.BUTTON_NEGATIVE: { 128 finish(); 129 } 130 break; 131 } 132 } 133 134 @Override onStart()135 protected void onStart() { 136 super.onStart(); 137 mReceiver.register(); 138 139 final int wifiState = mWiFiManager.getWifiState(); 140 141 switch (mState) { 142 case STATE_ENABLE: { 143 switch (wifiState) { 144 case WifiManager.WIFI_STATE_ENABLED: { 145 setResult(RESULT_OK); 146 finish(); 147 } return; 148 149 case WifiManager.WIFI_STATE_ENABLING: { 150 mState = STATE_ENABLING; 151 scheduleToggleTimeout(); 152 } break; 153 } 154 } break; 155 156 case STATE_DISABLE: { 157 switch (wifiState) { 158 case WifiManager.WIFI_STATE_DISABLED: { 159 setResult(RESULT_OK); 160 finish(); 161 } 162 return; 163 164 case WifiManager.WIFI_STATE_ENABLING: { 165 mState = STATE_DISABLING; 166 scheduleToggleTimeout(); 167 } 168 break; 169 } 170 } break; 171 172 case STATE_ENABLING: { 173 switch (wifiState) { 174 case WifiManager.WIFI_STATE_ENABLED: { 175 setResult(RESULT_OK); 176 finish(); 177 } return; 178 179 case WifiManager.WIFI_STATE_ENABLING: { 180 scheduleToggleTimeout(); 181 } break; 182 183 case WifiManager.WIFI_STATE_DISABLED: 184 case WifiManager.WIFI_STATE_DISABLING: { 185 mState = STATE_ENABLE; 186 } break; 187 } 188 } break; 189 190 case STATE_DISABLING: { 191 switch (wifiState) { 192 case WifiManager.WIFI_STATE_DISABLED: { 193 setResult(RESULT_OK); 194 finish(); 195 } return; 196 197 case WifiManager.WIFI_STATE_DISABLING: { 198 scheduleToggleTimeout(); 199 } break; 200 201 case WifiManager.WIFI_STATE_ENABLED: 202 case WifiManager.WIFI_STATE_ENABLING: { 203 mState = STATE_DISABLE; 204 } break; 205 } 206 } break; 207 } 208 209 updateUi(); 210 } 211 212 @Override onStop()213 protected void onStop() { 214 mReceiver.unregister(); 215 unscheduleToggleTimeout(); 216 super.onStop(); 217 } 218 219 @VisibleForTesting getAppLabel()220 protected CharSequence getAppLabel() { 221 String packageName; 222 try { 223 packageName = mActivityManager.getLaunchedFromPackage(getActivityToken()); 224 if (TextUtils.isEmpty(packageName)) { 225 Log.d(LOG_TAG, "Package name is null"); 226 return null; 227 } 228 } catch (RemoteException e) { 229 Log.e(LOG_TAG, "Can not get the package from activity manager"); 230 return null; 231 } 232 233 try { 234 ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo( 235 packageName, 0); 236 return applicationInfo.loadSafeLabel(getPackageManager(), 237 PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, PackageItemInfo.SAFE_LABEL_FLAG_TRIM 238 | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE); 239 } catch (PackageManager.NameNotFoundException e) { 240 Log.e(LOG_TAG, "Couldn't find app with package name " + packageName); 241 return null; 242 } 243 } 244 updateUi()245 private void updateUi() { 246 if (mLastUpdateState == mState) { 247 return; 248 } 249 mLastUpdateState = mState; 250 251 switch (mState) { 252 case STATE_ENABLE: { 253 mAlertParams.mPositiveButtonText = getString(R.string.allow); 254 mAlertParams.mPositiveButtonListener = this; 255 mAlertParams.mNegativeButtonText = getString(R.string.deny); 256 mAlertParams.mNegativeButtonListener = this; 257 mAlertParams.mMessage = getString(R.string.wifi_ask_enable, mAppLabel); 258 } break; 259 260 case STATE_ENABLING: { 261 // Params set button text only if non-null, but we want a null 262 // button text to hide the button, so reset the controller directly. 263 mAlert.setButton(DialogInterface.BUTTON_POSITIVE, null, null, null); 264 mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, null, null, null); 265 mAlertParams.mPositiveButtonText = null; 266 mAlertParams.mPositiveButtonListener = null; 267 mAlertParams.mNegativeButtonText = null; 268 mAlertParams.mNegativeButtonListener = null; 269 mAlertParams.mMessage = getString(R.string.wifi_starting); 270 } break; 271 272 case STATE_DISABLE: { 273 mAlertParams.mPositiveButtonText = getString(R.string.allow); 274 mAlertParams.mPositiveButtonListener = this; 275 mAlertParams.mNegativeButtonText = getString(R.string.deny); 276 mAlertParams.mNegativeButtonListener = this; 277 mAlertParams.mMessage = getString(R.string.wifi_ask_disable, mAppLabel); 278 } break; 279 280 case STATE_DISABLING: { 281 // Params set button text only if non-null, but we want a null 282 // button text to hide the button, so reset the controller directly. 283 mAlert.setButton(DialogInterface.BUTTON_POSITIVE, null, null, null); 284 mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, null, null, null); 285 mAlertParams.mPositiveButtonText = null; 286 mAlertParams.mPositiveButtonListener = null; 287 mAlertParams.mNegativeButtonText = null; 288 mAlertParams.mNegativeButtonListener = null; 289 mAlertParams.mMessage = getString(R.string.wifi_stopping); 290 } break; 291 } 292 293 setupAlert(); 294 } 295 296 @Override dismiss()297 public void dismiss() { 298 // Clicking on the dialog buttons dismisses the dialog and finishes 299 // the activity but we want to finish after the WiFi state changed. 300 } 301 scheduleToggleTimeout()302 private void scheduleToggleTimeout() { 303 getWindow().getDecorView().postDelayed(mTimeoutCommand, TOGGLE_TIMEOUT_MILLIS); 304 } 305 unscheduleToggleTimeout()306 private void unscheduleToggleTimeout() { 307 getWindow().getDecorView().removeCallbacks(mTimeoutCommand); 308 } 309 310 private final class StateChangeReceiver extends BroadcastReceiver { 311 private final IntentFilter mFilter = new IntentFilter( 312 WifiManager.WIFI_STATE_CHANGED_ACTION); 313 register()314 public void register() { 315 registerReceiver(this, mFilter); 316 } 317 unregister()318 public void unregister() { 319 unregisterReceiver(this); 320 } 321 onReceive(Context context, Intent intent)322 public void onReceive(Context context, Intent intent) { 323 Activity activity = RequestToggleWiFiActivity.this; 324 if (activity.isFinishing() || activity.isDestroyed()) { 325 return; 326 } 327 final int currentState = mWiFiManager.getWifiState(); 328 switch (currentState) { 329 case WifiManager.WIFI_STATE_ENABLED: 330 case WifiManager.WIFI_STATE_DISABLED: { 331 if (mState == STATE_ENABLING || mState == STATE_DISABLING) { 332 RequestToggleWiFiActivity.this.setResult(Activity.RESULT_OK); 333 finish(); 334 } 335 } break; 336 } 337 } 338 } 339 } 340