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