116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang/*
216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * Copyright (C) 2016 The Android Open Source Project
316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang *
416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * Licensed under the Apache License, Version 2.0 (the "License");
516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * you may not use this file except in compliance with the License.
616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * You may obtain a copy of the License at
716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang *
816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang *      http://www.apache.org/licenses/LICENSE-2.0
916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang *
1016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * Unless required by applicable law or agreed to in writing, software
1116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * distributed under the License is distributed on an "AS IS" BASIS,
1216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * See the License for the specific language governing permissions and
1416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang * limitations under the License.
1516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang */
1616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changpackage com.android.settings.vpn2;
1716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
1816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.app.AlertDialog;
1916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.app.AppOpsManager;
2016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.app.Dialog;
2116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.app.DialogFragment;
2216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.content.Context;
2316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.content.DialogInterface;
2416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.content.pm.PackageInfo;
2516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.content.pm.PackageManager;
2616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.content.pm.PackageManager.NameNotFoundException;
2716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.net.ConnectivityManager;
28cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Leeimport android.os.Build;
2916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.os.Bundle;
3016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.os.UserHandle;
3116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.os.UserManager;
321b8855b2115f26a418150e3eef9b3773e608b241Victor Changimport android.provider.Settings;
3316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.support.v7.preference.Preference;
3416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport android.util.Log;
3516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
3616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport com.android.internal.logging.MetricsProto.MetricsEvent;
3716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport com.android.internal.net.VpnConfig;
3816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport com.android.settings.R;
3916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport com.android.settings.SettingsPreferenceFragment;
4052541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Leeimport com.android.settings.Utils;
4152541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Leeimport com.android.settingslib.RestrictedLockUtils;
4216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport com.android.settingslib.RestrictedSwitchPreference;
4316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport com.android.settingslib.RestrictedPreference;
4416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
4516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport java.util.List;
4616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
4716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changimport static android.app.AppOpsManager.OP_ACTIVATE_VPN;
4816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
4916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Changpublic class AppManagementFragment extends SettingsPreferenceFragment
5052541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
5116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
5216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private static final String TAG = "AppManagementFragment";
5316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
5416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private static final String ARG_PACKAGE_NAME = "package";
5516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
5616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private static final String KEY_VERSION = "version";
5716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private static final String KEY_ALWAYS_ON_VPN = "always_on_vpn";
5816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private static final String KEY_FORGET_VPN = "forget_vpn";
5916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
6016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private AppOpsManager mAppOpsManager;
6116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private PackageManager mPackageManager;
6216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private ConnectivityManager mConnectivityManager;
6316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
6416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    // VPN app info
6516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private final int mUserId = UserHandle.myUserId();
6616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private int mPackageUid;
6716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private String mPackageName;
6816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private PackageInfo mPackageInfo;
6916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private String mVpnLabel;
7016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
7116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    // UI preference
7216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private Preference mPreferenceVersion;
7316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private RestrictedSwitchPreference mPreferenceAlwaysOn;
7416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private RestrictedPreference mPreferenceForget;
7516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
7616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    // Listener
7716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private final AppDialogFragment.Listener mForgetVpnDialogFragmentListener =
7816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            new AppDialogFragment.Listener() {
7916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        @Override
8016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        public void onForget() {
8116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            // Unset always-on-vpn when forgetting the VPN
8216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            if (isVpnAlwaysOn()) {
83fcb0d74a3c107f9bf8165a82850d7a5eef0ca5f3Victor Chang                setAlwaysOnVpn(false);
8416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            }
8516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            // Also dismiss and go back to VPN list
8616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            finish();
8716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
8816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
8916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        @Override
9016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        public void onCancel() {
9116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            // do nothing
9216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
9316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    };
9416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
9516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    public static void show(Context context, AppPreference pref) {
9616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        Bundle args = new Bundle();
9716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        args.putString(ARG_PACKAGE_NAME, pref.getPackageName());
9816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        Utils.startWithFragmentAsUser(context, AppManagementFragment.class.getName(), args, -1,
9916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                pref.getLabel(), false, new UserHandle(pref.getUserId()));
10016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
10116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
10216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    @Override
10316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    public void onCreate(Bundle savedState) {
10416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        super.onCreate(savedState);
10516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        addPreferencesFromResource(R.xml.vpn_app_management);
10616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
10716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mPackageManager = getContext().getPackageManager();
10816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
10916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mConnectivityManager = getContext().getSystemService(ConnectivityManager.class);
11016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
11116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mPreferenceVersion = findPreference(KEY_VERSION);
11216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mPreferenceAlwaysOn = (RestrictedSwitchPreference) findPreference(KEY_ALWAYS_ON_VPN);
11316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mPreferenceForget = (RestrictedPreference) findPreference(KEY_FORGET_VPN);
11416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
11552541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        mPreferenceAlwaysOn.setOnPreferenceChangeListener(this);
11616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mPreferenceForget.setOnPreferenceClickListener(this);
11716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
11816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
11916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    @Override
12016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    public void onResume() {
12116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        super.onResume();
12216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
12316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        boolean isInfoLoaded = loadInfo();
12416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (isInfoLoaded) {
12516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            mPreferenceVersion.setTitle(
12616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                    getPrefContext().getString(R.string.vpn_version, mPackageInfo.versionName));
12716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            updateUI();
12816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        } else {
12916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            finish();
13016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
13116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
13216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
13316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    @Override
13416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    public boolean onPreferenceClick(Preference preference) {
13516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        String key = preference.getKey();
13616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        switch (key) {
13716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            case KEY_FORGET_VPN:
13816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                return onForgetVpnClick();
13916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            default:
14016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                Log.w(TAG, "unknown key is clicked: " + key);
14116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                return false;
14216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
14316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
14416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
14516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    @Override
14652541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee    public boolean onPreferenceChange(Preference preference, Object newValue) {
14752541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        switch (preference.getKey()) {
14852541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            case KEY_ALWAYS_ON_VPN:
14952541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                return onAlwaysOnVpnClick((Boolean) newValue);
15052541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            default:
15152541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                Log.w(TAG, "unknown key is clicked: " + preference.getKey());
15252541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                return false;
15352541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        }
15452541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee    }
15552541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee
15652541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee    @Override
15716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    protected int getMetricsCategory() {
15816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        return MetricsEvent.VPN;
15916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
16016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
16116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private boolean onForgetVpnClick() {
162cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        updateRestrictedViews();
16352541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        if (!mPreferenceForget.isEnabled()) {
16452541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            return false;
16552541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        }
16616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        AppDialogFragment.show(this, mForgetVpnDialogFragmentListener, mPackageInfo, mVpnLabel,
16716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                true /* editing */, true);
16816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        return true;
16916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
17016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
17152541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee    private boolean onAlwaysOnVpnClick(final boolean isChecked) {
17216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (isChecked && isLegacyVpnLockDownOrAnotherPackageAlwaysOn()) {
17316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            // Show dialog if user replace always-on-vpn package and show not checked first
17416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            ReplaceExistingVpnFragment.show(this);
17552541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            return false;
17616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        } else {
17752541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            return setAlwaysOnVpnByUI(isChecked);
17816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
17916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
18016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
18152541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee    private boolean setAlwaysOnVpnByUI(boolean isEnabled) {
182cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        updateRestrictedViews();
18352541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        if (!mPreferenceAlwaysOn.isEnabled()) {
18452541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            return false;
18552541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        }
18616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        // Only clear legacy lockdown vpn in system user.
18716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (mUserId == UserHandle.USER_SYSTEM) {
18816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            VpnUtils.clearLockdownVpn(getContext());
18916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
190fcb0d74a3c107f9bf8165a82850d7a5eef0ca5f3Victor Chang        final boolean success = setAlwaysOnVpn(isEnabled);
19152541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        if (isEnabled && (!success || !isVpnAlwaysOn())) {
19228210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee            CannotConnectFragment.show(this, mVpnLabel);
19328210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        }
19452541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        return success;
19516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
19616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
197fcb0d74a3c107f9bf8165a82850d7a5eef0ca5f3Victor Chang    private boolean setAlwaysOnVpn(boolean isEnabled) {
198fcb0d74a3c107f9bf8165a82850d7a5eef0ca5f3Victor Chang         return mConnectivityManager.setAlwaysOnVpnPackageForUser(mUserId,
199fcb0d74a3c107f9bf8165a82850d7a5eef0ca5f3Victor Chang                isEnabled ? mPackageName : null, /* lockdownEnabled */ false);
200fcb0d74a3c107f9bf8165a82850d7a5eef0ca5f3Victor Chang    }
201fcb0d74a3c107f9bf8165a82850d7a5eef0ca5f3Victor Chang
202cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee    private boolean checkTargetVersion() {
203cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
204cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee            return true;
205cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        }
206cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        final int targetSdk = mPackageInfo.applicationInfo.targetSdkVersion;
207cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        if (targetSdk >= Build.VERSION_CODES.N) {
208cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee            return true;
209cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        }
210cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        if (Log.isLoggable(TAG, Log.DEBUG)) {
211cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee            Log.d(TAG, "Package " + mPackageName + " targets SDK version " + targetSdk + "; must"
212cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee                    + " target at least " + Build.VERSION_CODES.N + " to use always-on.");
213cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        }
214cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee        return false;
215cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee    }
216cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee
21716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private void updateUI() {
21816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (isAdded()) {
21916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            mPreferenceAlwaysOn.setChecked(isVpnAlwaysOn());
220cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee            updateRestrictedViews();
22152541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        }
22252541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee    }
22352541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee
224cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee    private void updateRestrictedViews() {
22552541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee        if (isAdded()) {
22652541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            mPreferenceAlwaysOn.checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_VPN,
22752541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                    mUserId);
22852541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee            mPreferenceForget.checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_VPN,
22952541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                    mUserId);
230cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee
23152247f69a579c189a881c53d2d021eeffa1609a8Victor Chang            if (checkTargetVersion()) {
23252247f69a579c189a881c53d2d021eeffa1609a8Victor Chang                // setSummary doesn't override the admin message when user restriction is applied
23352247f69a579c189a881c53d2d021eeffa1609a8Victor Chang                mPreferenceAlwaysOn.setSummary(null);
23452247f69a579c189a881c53d2d021eeffa1609a8Victor Chang                // setEnabled is not required here, as checkRestrictionAndSetDisabled
23552247f69a579c189a881c53d2d021eeffa1609a8Victor Chang                // should have refreshed the enable state.
23652247f69a579c189a881c53d2d021eeffa1609a8Victor Chang            } else {
237cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee                mPreferenceAlwaysOn.setEnabled(false);
23852247f69a579c189a881c53d2d021eeffa1609a8Victor Chang                mPreferenceAlwaysOn.setSummary(R.string.vpn_not_supported_by_this_app);
239cfc0fb02c1d3ff229323660ca432c23cfc8d11b0Robin Lee            }
24016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
24116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
24216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
24316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private String getAlwaysOnVpnPackage() {
24416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        return mConnectivityManager.getAlwaysOnVpnPackageForUser(mUserId);
24516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
24616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
24716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private boolean isVpnAlwaysOn() {
24816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        return mPackageName.equals(getAlwaysOnVpnPackage());
24916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
25016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
25116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    /**
25216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang     * @return false if the intent doesn't contain an existing package or can't retrieve activated
25316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang     * vpn info.
25416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang     */
25516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private boolean loadInfo() {
25616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        final Bundle args = getArguments();
25716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (args == null) {
25816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            Log.e(TAG, "empty bundle");
25916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            return false;
26016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
26116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
26216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        mPackageName = args.getString(ARG_PACKAGE_NAME);
26316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (mPackageName == null) {
26416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            Log.e(TAG, "empty package name");
26516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            return false;
26616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
26716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
26816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        try {
26916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            mPackageUid = mPackageManager.getPackageUid(mPackageName, /* PackageInfoFlags */ 0);
27016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            mPackageInfo = mPackageManager.getPackageInfo(mPackageName, /* PackageInfoFlags */ 0);
27116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            mVpnLabel = VpnConfig.getVpnLabel(getPrefContext(), mPackageName).toString();
27216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        } catch (NameNotFoundException nnfe) {
27316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            Log.e(TAG, "package not found", nnfe);
27416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            return false;
27516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
27616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
27716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (!isVpnActivated()) {
27816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            Log.e(TAG, "package didn't register VPN profile");
27916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            return false;
28016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
28116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
28216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        return true;
28316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
28416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
28516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private boolean isVpnActivated() {
28616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        final List<AppOpsManager.PackageOps> apps = mAppOpsManager.getOpsForPackage(mPackageUid,
28716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                mPackageName, new int[]{OP_ACTIVATE_VPN});
28816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        return apps != null && apps.size() > 0 && apps.get(0) != null;
28916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
29016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
29116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    private boolean isLegacyVpnLockDownOrAnotherPackageAlwaysOn() {
29216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        if (mUserId == UserHandle.USER_SYSTEM) {
29316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            String lockdownKey = VpnUtils.getLockdownVpn();
29416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            if (lockdownKey != null) {
29516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                return true;
29616da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            }
29716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
29816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
29916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        return getAlwaysOnVpnPackage() != null && !isVpnAlwaysOn();
30016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
30116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
30228210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee    public static class CannotConnectFragment extends DialogFragment {
30328210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        private static final String TAG = "CannotConnect";
30428210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        private static final String ARG_VPN_LABEL = "label";
30528210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee
30628210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        public static void show(AppManagementFragment parent, String vpnLabel) {
30728210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee            if (parent.getFragmentManager().findFragmentByTag(TAG) == null) {
30828210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                final Bundle args = new Bundle();
30928210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                args.putString(ARG_VPN_LABEL, vpnLabel);
31028210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee
31128210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                final DialogFragment frag = new CannotConnectFragment();
31228210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                frag.setArguments(args);
31328210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                frag.show(parent.getFragmentManager(), TAG);
3141b8855b2115f26a418150e3eef9b3773e608b241Victor Chang            }
3151b8855b2115f26a418150e3eef9b3773e608b241Victor Chang        }
3161b8855b2115f26a418150e3eef9b3773e608b241Victor Chang
31728210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        @Override
31828210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        public Dialog onCreateDialog(Bundle savedInstanceState) {
31928210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee            final String vpnLabel = getArguments().getString(ARG_VPN_LABEL);
32028210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee            return new AlertDialog.Builder(getActivity())
32128210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                    .setTitle(getActivity().getString(R.string.vpn_cant_connect_title, vpnLabel))
32228210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                    .setMessage(getActivity().getString(R.string.vpn_cant_connect_message))
32328210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                    .setPositiveButton(R.string.okay, null)
32428210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                    .create();
32528210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        }
3261b8855b2115f26a418150e3eef9b3773e608b241Victor Chang    }
3271b8855b2115f26a418150e3eef9b3773e608b241Victor Chang
32816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    public static class ReplaceExistingVpnFragment extends DialogFragment
32916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            implements DialogInterface.OnClickListener {
33028210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee        private static final String TAG = "ReplaceExistingVpn";
33116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
33216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        public static void show(AppManagementFragment parent) {
33328210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee            if (parent.getFragmentManager().findFragmentByTag(TAG) == null) {
33428210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                final ReplaceExistingVpnFragment frag = new ReplaceExistingVpnFragment();
33528210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                frag.setTargetFragment(parent, 0);
33628210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee                frag.show(parent.getFragmentManager(), TAG);
33728210677099c70e0e19f42f2ad3abe5ad78ee8b8Robin Lee            }
33816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
33916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
34016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        @Override
34116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        public Dialog onCreateDialog(Bundle savedInstanceState) {
34216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            return new AlertDialog.Builder(getActivity())
34316da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                    .setTitle(R.string.vpn_replace_always_on_vpn_title)
34416da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                    .setMessage(getActivity().getString(R.string.vpn_replace_always_on_vpn_message))
34516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                    .setNegativeButton(getActivity().getString(R.string.vpn_cancel), null)
346c9bf2ac95e346f0c279436aa1f37d5bea6d2443eRobin Lee                    .setPositiveButton(getActivity().getString(R.string.vpn_replace), this)
34716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                    .create();
34816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
34916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang
35016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        @Override
35116da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        public void onClick(DialogInterface dialog, int which) {
35216da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            if (getTargetFragment() instanceof AppManagementFragment) {
35352541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                final AppManagementFragment target = (AppManagementFragment) getTargetFragment();
35452541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                if (target.setAlwaysOnVpnByUI(true)) {
35552541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                    target.updateUI();
35652541536d0d4c8f78be26f94fa8f0cf297bde1c4Robin Lee                }
35716da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang            }
35816da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang        }
35916da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang    }
36016da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang}
361