1 /* 2 * Copyright (C) 2019 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.car.settings.wifi; 18 19 import android.annotation.NonNull; 20 import android.app.Activity; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageManager; 27 import android.net.wifi.WifiManager; 28 import android.os.Bundle; 29 30 import androidx.annotation.VisibleForTesting; 31 import androidx.fragment.app.FragmentActivity; 32 33 import com.android.car.settings.R; 34 import com.android.car.settings.common.ConfirmationDialogFragment; 35 import com.android.car.settings.common.Logger; 36 import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; 37 38 /** 39 * Code drop from {@link com.android.settings.wifi.RequestToggleWiFiActivity}. 40 * 41 * This {@link Activity} handles requests to toggle WiFi by collecting user 42 * consent and waiting until the state change is completed. 43 */ 44 public class WifiRequestToggleActivity extends FragmentActivity { 45 private static final Logger LOG = new Logger(WifiRequestToggleActivity.class); 46 private static final long TOGGLE_TIMEOUT_MILLIS = 10000; // 10 sec 47 48 private static final int STATE_UNKNOWN = -1; 49 private static final int STATE_ENABLE = 1; 50 private static final int STATE_ENABLING = 2; 51 private static final int STATE_DISABLE = 3; 52 private static final int STATE_DISABLING = 4; 53 54 private final StateChangeReceiver mReceiver = new StateChangeReceiver(); 55 56 private final Runnable mTimeoutCommand = () -> { 57 if (!isFinishing() && !isDestroyed()) { 58 finish(); 59 } 60 }; 61 62 @NonNull 63 private CarWifiManager mCarWifiManager; 64 @NonNull 65 private CharSequence mAppLabel; 66 67 private ConfirmationDialogFragment mDialog; 68 private int mAction = STATE_UNKNOWN; 69 private int mState = STATE_UNKNOWN; 70 71 @VisibleForTesting 72 final ConfirmationDialogFragment.ConfirmListener mConfirmListener = arguments -> { 73 switch (mState) { 74 case STATE_ENABLE: 75 mCarWifiManager.setWifiEnabled(true); 76 mState = STATE_ENABLING; 77 updateUi(); 78 scheduleToggleTimeout(); 79 break; 80 case STATE_DISABLE: 81 mCarWifiManager.setWifiEnabled(false); 82 mState = STATE_DISABLING; 83 updateUi(); 84 scheduleToggleTimeout(); 85 break; 86 } 87 setResult(RESULT_OK); 88 }; 89 90 @VisibleForTesting 91 final ConfirmationDialogFragment.RejectListener mRejectListener = arguments -> { 92 finish(); 93 }; 94 95 @VisibleForTesting 96 final ConfirmationDialogFragment.DismissListener mDismissListener = 97 (arguments, positiveResult) -> finish(); 98 99 @Override onCreate(Bundle savedInstanceState)100 protected void onCreate(Bundle savedInstanceState) { 101 super.onCreate(savedInstanceState); 102 getLifecycle().addObserver(new HideNonSystemOverlayMixin(this)); 103 104 mCarWifiManager = new CarWifiManager(getApplicationContext(), getLifecycle()); 105 106 setResult(Activity.RESULT_CANCELED); 107 108 try { 109 PackageManager pm = getPackageManager(); 110 ApplicationInfo ai = pm.getApplicationInfo(getLaunchedFromPackage(), /* flags= */ 0); 111 mAppLabel = pm.getApplicationLabel(ai); 112 } catch (PackageManager.NameNotFoundException e) { 113 LOG.e("Couldn't find app with package name " + getLaunchedFromPackage()); 114 finish(); 115 return; 116 } 117 118 mAction = extractActionState(); 119 if (mAction == STATE_UNKNOWN) { 120 finish(); 121 } 122 } 123 124 @Override onStart()125 protected void onStart() { 126 super.onStart(); 127 128 mReceiver.register(); 129 130 final int wifiState = mCarWifiManager.getWifiState(); 131 132 switch (mAction) { 133 case STATE_ENABLE: 134 switch (wifiState) { 135 case WifiManager.WIFI_STATE_ENABLED: 136 setResult(RESULT_OK); 137 finish(); 138 return; 139 140 case WifiManager.WIFI_STATE_ENABLING: 141 mState = STATE_ENABLING; 142 scheduleToggleTimeout(); 143 break; 144 default: 145 mState = mAction; 146 } 147 break; 148 149 case STATE_DISABLE: 150 switch (wifiState) { 151 case WifiManager.WIFI_STATE_DISABLED: 152 setResult(RESULT_OK); 153 finish(); 154 return; 155 156 case WifiManager.WIFI_STATE_DISABLING: 157 mState = STATE_DISABLING; 158 scheduleToggleTimeout(); 159 break; 160 default: 161 mState = mAction; 162 } 163 break; 164 } 165 updateUi(); 166 } 167 168 @Override onStop()169 protected void onStop() { 170 super.onStop(); 171 mReceiver.unregister(); 172 unscheduleToggleTimeout(); 173 if (mDialog != null) { 174 mDialog.dismiss(); 175 } 176 } 177 updateUi()178 private void updateUi() { 179 if (mDialog != null) { 180 mDialog.dismiss(); 181 } 182 183 ConfirmationDialogFragment.Builder builder = 184 new ConfirmationDialogFragment.Builder(/* context= */ this).setDismissListener( 185 mDismissListener); 186 switch (mState) { 187 case STATE_ENABLE: 188 builder.setPositiveButton(R.string.allow, mConfirmListener); 189 builder.setNegativeButton(R.string.deny, mRejectListener); 190 builder.setMessage(getString(R.string.wifi_ask_enable, mAppLabel)); 191 break; 192 193 case STATE_ENABLING: 194 builder.setMessage(getString(R.string.wifi_starting, mAppLabel)); 195 break; 196 197 case STATE_DISABLE: 198 builder.setPositiveButton(R.string.allow, mConfirmListener); 199 builder.setNegativeButton(R.string.deny, mRejectListener); 200 builder.setMessage(getString(R.string.wifi_ask_disable, mAppLabel)); 201 break; 202 203 case STATE_DISABLING: 204 builder.setMessage(getString(R.string.wifi_stopping, mAppLabel)); 205 break; 206 } 207 mDialog = builder.build(); 208 mDialog.show(getSupportFragmentManager(), ConfirmationDialogFragment.TAG); 209 } 210 scheduleToggleTimeout()211 private void scheduleToggleTimeout() { 212 getWindow().getDecorView().postDelayed(mTimeoutCommand, TOGGLE_TIMEOUT_MILLIS); 213 } 214 unscheduleToggleTimeout()215 private void unscheduleToggleTimeout() { 216 getWindow().getDecorView().removeCallbacks(mTimeoutCommand); 217 } 218 extractActionState()219 private int extractActionState() { 220 String action = getIntent().getAction(); 221 switch (action) { 222 case WifiManager.ACTION_REQUEST_ENABLE: 223 return STATE_ENABLE; 224 225 case WifiManager.ACTION_REQUEST_DISABLE: 226 return STATE_DISABLE; 227 228 default: 229 return STATE_UNKNOWN; 230 } 231 } 232 233 private final class StateChangeReceiver extends BroadcastReceiver { 234 private final IntentFilter mFilter = new IntentFilter( 235 WifiManager.WIFI_STATE_CHANGED_ACTION); 236 register()237 public void register() { 238 registerReceiver(/* receiver= */ this, mFilter); 239 } 240 unregister()241 public void unregister() { 242 unregisterReceiver(/* receiver= */ this); 243 } 244 onReceive(Context context, Intent intent)245 public void onReceive(Context context, Intent intent) { 246 Activity activity = WifiRequestToggleActivity.this; 247 if (activity.isFinishing() || activity.isDestroyed()) { 248 return; 249 } 250 int currentState = mCarWifiManager.getWifiState(); 251 switch (currentState) { 252 case WifiManager.WIFI_STATE_ENABLED: 253 case WifiManager.WIFI_STATE_DISABLED: 254 if (mState == STATE_ENABLING || mState == STATE_DISABLING) { 255 activity.setResult(Activity.RESULT_OK); 256 finish(); 257 } 258 break; 259 } 260 } 261 } 262 } 263