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