1/*
2 * Copyright (C) 2014 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 */
16package com.android.systemui.statusbar.policy;
17
18import android.app.ActivityManager;
19import android.app.admin.DevicePolicyManager;
20import android.content.Context;
21import android.content.pm.PackageManager.NameNotFoundException;
22import android.net.ConnectivityManager;
23import android.net.ConnectivityManager.NetworkCallback;
24import android.net.IConnectivityManager;
25import android.net.Network;
26import android.net.NetworkCapabilities;
27import android.net.NetworkRequest;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.text.TextUtils;
31import android.util.Log;
32
33import com.android.internal.net.VpnConfig;
34
35import java.io.FileDescriptor;
36import java.io.PrintWriter;
37import java.util.ArrayList;
38
39public class SecurityControllerImpl implements SecurityController {
40
41    private static final String TAG = "SecurityController";
42    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
43
44    private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
45            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
46            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
47            .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
48            .build();
49    private static final int NO_NETWORK = -1;
50
51    private final Context mContext;
52    private final ConnectivityManager mConnectivityManager;
53    private final IConnectivityManager mConnectivityService = IConnectivityManager.Stub.asInterface(
54                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
55    private final DevicePolicyManager mDevicePolicyManager;
56    private final ArrayList<SecurityControllerCallback> mCallbacks
57            = new ArrayList<SecurityControllerCallback>();
58
59    private VpnConfig mVpnConfig;
60    private String mVpnName;
61    private int mCurrentVpnNetworkId = NO_NETWORK;
62    private int mCurrentUserId;
63
64    public SecurityControllerImpl(Context context) {
65        mContext = context;
66        mDevicePolicyManager = (DevicePolicyManager)
67                context.getSystemService(Context.DEVICE_POLICY_SERVICE);
68        mConnectivityManager = (ConnectivityManager)
69                context.getSystemService(Context.CONNECTIVITY_SERVICE);
70
71        // TODO: re-register network callback on user change.
72        mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
73        mCurrentUserId = ActivityManager.getCurrentUser();
74    }
75
76    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
77        pw.println("SecurityController state:");
78        pw.print("  mCurrentVpnNetworkId="); pw.println(mCurrentVpnNetworkId);
79        pw.print("  mVpnConfig="); pw.println(mVpnConfig);
80        pw.print("  mVpnName="); pw.println(mVpnName);
81    }
82
83    @Override
84    public boolean hasDeviceOwner() {
85        return !TextUtils.isEmpty(mDevicePolicyManager.getDeviceOwner());
86    }
87
88    @Override
89    public boolean hasProfileOwner() {
90        return !TextUtils.isEmpty(mDevicePolicyManager.getProfileOwnerNameAsUser(mCurrentUserId));
91    }
92
93    @Override
94    public String getDeviceOwnerName() {
95        return mDevicePolicyManager.getDeviceOwnerName();
96    }
97
98    @Override
99    public String getProfileOwnerName() {
100        return mDevicePolicyManager.getProfileOwnerNameAsUser(mCurrentUserId);
101    }
102
103
104    @Override
105    public boolean isVpnEnabled() {
106        return mCurrentVpnNetworkId != NO_NETWORK;
107    }
108
109    @Override
110    public boolean isLegacyVpn() {
111        return mVpnConfig.legacy;
112    }
113
114    @Override
115    public String getVpnApp() {
116        return mVpnName;
117    }
118
119    @Override
120    public String getLegacyVpnName() {
121        return mVpnConfig.session;
122    }
123
124    @Override
125    public void disconnectFromVpn() {
126        try {
127            if (isLegacyVpn()) {
128                mConnectivityService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
129            } else {
130                // Prevent this app from initiating VPN connections in the future without user
131                // intervention.
132                mConnectivityService.setVpnPackageAuthorization(false);
133
134                mConnectivityService.prepareVpn(mVpnConfig.user, VpnConfig.LEGACY_VPN);
135            }
136        } catch (Exception e) {
137            Log.e(TAG, "Unable to disconnect from VPN", e);
138        }
139    }
140
141    @Override
142    public void removeCallback(SecurityControllerCallback callback) {
143        if (callback == null) return;
144        if (DEBUG) Log.d(TAG, "removeCallback " + callback);
145        mCallbacks.remove(callback);
146    }
147
148    @Override
149    public void addCallback(SecurityControllerCallback callback) {
150        if (callback == null || mCallbacks.contains(callback)) return;
151        if (DEBUG) Log.d(TAG, "addCallback " + callback);
152        mCallbacks.add(callback);
153    }
154
155    @Override
156    public void onUserSwitched(int newUserId) {
157        mCurrentUserId = newUserId;
158        fireCallbacks();
159    }
160
161    private void setCurrentNetid(int netId) {
162        if (netId != mCurrentVpnNetworkId) {
163            mCurrentVpnNetworkId = netId;
164            updateState();
165            fireCallbacks();
166        }
167    }
168
169    private void fireCallbacks() {
170        for (SecurityControllerCallback callback : mCallbacks) {
171            callback.onStateChanged();
172        }
173    }
174
175    private void updateState() {
176        try {
177            mVpnConfig = mConnectivityService.getVpnConfig();
178
179            if (mVpnConfig != null && !mVpnConfig.legacy) {
180                mVpnName = VpnConfig.getVpnLabel(mContext, mVpnConfig.user).toString();
181            }
182        } catch (RemoteException | NameNotFoundException e) {
183            Log.w(TAG, "Unable to get current VPN", e);
184        }
185    }
186
187    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
188        @Override
189        public void onAvailable(Network network) {
190            NetworkCapabilities networkCapabilities =
191                    mConnectivityManager.getNetworkCapabilities(network);
192            if (DEBUG) Log.d(TAG, "onAvailable " + network.netId + " : " + networkCapabilities);
193            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
194                setCurrentNetid(network.netId);
195            }
196        };
197
198        // TODO Find another way to receive VPN lost.  This may be delayed depending on
199        // how long the VPN connection is held on to.
200        @Override
201        public void onLost(Network network) {
202            if (DEBUG) Log.d(TAG, "onLost " + network.netId);
203            if (mCurrentVpnNetworkId == network.netId) {
204                setCurrentNetid(NO_NETWORK);
205            }
206        };
207    };
208
209}
210