Skip to content
Snippets Groups Projects
Commit 782c1fd1 authored by Ray Chen's avatar Ray Chen Committed by Gerrit Code Review
Browse files

Merge changes from topic "VPN_ICON" into main

* changes:
  Adjust the NetworkRequest configuration used by SecurityController
  Design of the VPN icon reflects the invalidated status
  Update the VPN icon to reflect the validation status of the VPN network
parents 4c2e9d3c 7575405f
No related branches found
No related tags found
No related merge requests found
Showing with 220 additions and 5 deletions
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2023, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="17dp"
android:height="17dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M22,10h-2v8h2V10z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M22,20h-2v2h2V20z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2023, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="17dp"
android:height="17dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M22,10h-2v8h2V10z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M22,20h-2v2h2V20z"/>
</vector>
...@@ -118,15 +118,25 @@ public class StatusBarSignalPolicy implements SignalCallback, ...@@ -118,15 +118,25 @@ public class StatusBarSignalPolicy implements SignalCallback,
private void updateVpn() { private void updateVpn() {
boolean vpnVisible = mSecurityController.isVpnEnabled(); boolean vpnVisible = mSecurityController.isVpnEnabled();
int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); int vpnIconId = currentVpnIconId(
mSecurityController.isVpnBranded(),
mSecurityController.isVpnValidated());
mIconController.setIcon(mSlotVpn, vpnIconId, mIconController.setIcon(mSlotVpn, vpnIconId,
mContext.getResources().getString(R.string.accessibility_vpn_on)); mContext.getResources().getString(R.string.accessibility_vpn_on));
mIconController.setIconVisibility(mSlotVpn, vpnVisible); mIconController.setIconVisibility(mSlotVpn, vpnVisible);
} }
private int currentVpnIconId(boolean isBranded) { private int currentVpnIconId(boolean isBranded, boolean isValidated) {
return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; if (isBranded) {
return isValidated
? R.drawable.stat_sys_branded_vpn
: R.drawable.stat_sys_no_internet_branded_vpn;
} else {
return isValidated
? R.drawable.stat_sys_vpn_ic
: R.drawable.stat_sys_no_internet_vpn_ic;
}
} }
/** /**
......
...@@ -48,6 +48,8 @@ public interface SecurityController extends CallbackController<SecurityControlle ...@@ -48,6 +48,8 @@ public interface SecurityController extends CallbackController<SecurityControlle
boolean isNetworkLoggingEnabled(); boolean isNetworkLoggingEnabled();
boolean isVpnEnabled(); boolean isVpnEnabled();
boolean isVpnRestricted(); boolean isVpnRestricted();
/** Whether the VPN network is validated. */
boolean isVpnValidated();
/** Whether the VPN app should use branded VPN iconography. */ /** Whether the VPN app should use branded VPN iconography. */
boolean isVpnBranded(); boolean isVpnBranded();
String getPrimaryVpnName(); String getPrimaryVpnName();
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
*/ */
package com.android.systemui.statusbar.policy; package com.android.systemui.statusbar.policy;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.admin.DeviceAdminInfo; import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
...@@ -32,7 +35,9 @@ import android.content.pm.UserInfo; ...@@ -32,7 +35,9 @@ import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.net.VpnManager; import android.net.VpnManager;
import android.os.Handler; import android.os.Handler;
...@@ -76,7 +81,10 @@ public class SecurityControllerImpl implements SecurityController { ...@@ -76,7 +81,10 @@ public class SecurityControllerImpl implements SecurityController {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final NetworkRequest REQUEST = private static final NetworkRequest REQUEST =
new NetworkRequest.Builder().clearCapabilities().build(); new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_VPN)
.build();
private static final int NO_NETWORK = -1; private static final int NO_NETWORK = -1;
private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED"; private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED";
...@@ -99,6 +107,8 @@ public class SecurityControllerImpl implements SecurityController { ...@@ -99,6 +107,8 @@ public class SecurityControllerImpl implements SecurityController {
private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>(); private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>();
private int mCurrentUserId; private int mCurrentUserId;
private int mVpnUserId; private int mVpnUserId;
@GuardedBy("mNetworkProperties")
private final SparseArray<NetworkProperties> mNetworkProperties = new SparseArray<>();
// Key: userId, Value: whether the user has CACerts installed // Key: userId, Value: whether the user has CACerts installed
// Needs to be cached here since the query has to be asynchronous // Needs to be cached here since the query has to be asynchronous
...@@ -162,6 +172,21 @@ public class SecurityControllerImpl implements SecurityController { ...@@ -162,6 +172,21 @@ public class SecurityControllerImpl implements SecurityController {
pw.print(mCurrentVpns.valueAt(i).user); pw.print(mCurrentVpns.valueAt(i).user);
} }
pw.println("}"); pw.println("}");
pw.print(" mNetworkProperties={");
synchronized (mNetworkProperties) {
for (int i = 0; i < mNetworkProperties.size(); ++i) {
if (i > 0) {
pw.print(", ");
}
pw.print(mNetworkProperties.keyAt(i));
pw.print("={");
pw.print(mNetworkProperties.valueAt(i).interfaceName);
pw.print(", ");
pw.print(mNetworkProperties.valueAt(i).validated);
pw.print("}");
}
}
pw.println("}");
} }
@Override @Override
...@@ -303,6 +328,26 @@ public class SecurityControllerImpl implements SecurityController { ...@@ -303,6 +328,26 @@ public class SecurityControllerImpl implements SecurityController {
return isVpnPackageBranded(packageName); return isVpnPackageBranded(packageName);
} }
@Override
public boolean isVpnValidated() {
// Prioritize reporting the network status of the parent user.
final VpnConfig primaryVpnConfig = mCurrentVpns.get(mVpnUserId);
if (primaryVpnConfig != null) {
return getVpnValidationStatus(primaryVpnConfig);
}
// Identify any Unvalidated status in each active VPN network within other profiles.
for (int profileId : mUserManager.getEnabledProfileIds(mVpnUserId)) {
final VpnConfig vpnConfig = mCurrentVpns.get(profileId);
if (vpnConfig == null) {
continue;
}
if (!getVpnValidationStatus(vpnConfig)) {
return false;
}
}
return true;
}
@Override @Override
public boolean hasCACertInCurrentUser() { public boolean hasCACertInCurrentUser() {
Boolean hasCACerts = mHasCACerts.get(mCurrentUserId); Boolean hasCACerts = mHasCACerts.get(mCurrentUserId);
...@@ -491,11 +536,74 @@ public class SecurityControllerImpl implements SecurityController { ...@@ -491,11 +536,74 @@ public class SecurityControllerImpl implements SecurityController {
@Override @Override
public void onLost(Network network) { public void onLost(Network network) {
if (DEBUG) Log.d(TAG, "onLost " + network.getNetId()); if (DEBUG) Log.d(TAG, "onLost " + network.getNetId());
synchronized (mNetworkProperties) {
mNetworkProperties.delete(network.getNetId());
}
updateState(); updateState();
fireCallbacks(); fireCallbacks();
}; };
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
if (DEBUG) Log.d(TAG, "onCapabilitiesChanged " + network.getNetId());
final NetworkProperties properties;
synchronized (mNetworkProperties) {
properties = mNetworkProperties.get(network.getNetId());
}
// When a new network appears, the system first notifies the application about
// its capabilities through onCapabilitiesChanged. This initial notification
// will be skipped because the interface information is included in the
// subsequent onLinkPropertiesChanged call. After validating the network, the
// system might send another onCapabilitiesChanged notification if the network
// becomes validated.
if (properties == null) {
return;
}
final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
if (properties.validated != validated) {
properties.validated = validated;
fireCallbacks();
}
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
if (DEBUG) Log.d(TAG, "onLinkPropertiesChanged " + network.getNetId());
final String interfaceName = linkProperties.getInterfaceName();
if (interfaceName == null) {
Log.w(TAG, "onLinkPropertiesChanged event with null interface");
return;
}
synchronized (mNetworkProperties) {
final NetworkProperties properties = mNetworkProperties.get(network.getNetId());
if (properties == null) {
mNetworkProperties.put(
network.getNetId(),
new NetworkProperties(interfaceName, false));
} else {
properties.interfaceName = interfaceName;
}
}
}
}; };
/**
* Retrieve the validation status of the VPN network associated with the given VpnConfig.
*/
private boolean getVpnValidationStatus(@NonNull VpnConfig vpnConfig) {
synchronized (mNetworkProperties) {
// Find the network has the same interface as the VpnConfig
for (int i = 0; i < mNetworkProperties.size(); ++i) {
if (mNetworkProperties.valueAt(i).interfaceName.equals(vpnConfig.interfaze)) {
return mNetworkProperties.valueAt(i).validated;
}
}
}
// If no matching network is found, consider it validated.
return true;
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) { @Override public void onReceive(Context context, Intent intent) {
if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) { if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
...@@ -506,4 +614,17 @@ public class SecurityControllerImpl implements SecurityController { ...@@ -506,4 +614,17 @@ public class SecurityControllerImpl implements SecurityController {
} }
} }
}; };
/**
* A data class to hold specific Network properties received through the NetworkCallback.
*/
private static class NetworkProperties {
public String interfaceName;
public boolean validated;
NetworkProperties(@NonNull String interfaceName, boolean validated) {
this.interfaceName = interfaceName;
this.validated = validated;
}
}
} }
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy; package com.android.systemui.statusbar.policy;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
...@@ -213,7 +214,8 @@ public class SecurityControllerTest extends SysuiTestCase { ...@@ -213,7 +214,8 @@ public class SecurityControllerTest extends SysuiTestCase {
public void testNetworkRequest() { public void testNetworkRequest() {
verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat( verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
(NetworkRequest request) -> (NetworkRequest request) ->
request.equals(new NetworkRequest.Builder().clearCapabilities().build()) request.equals(new NetworkRequest.Builder()
.clearCapabilities().addTransportType(TRANSPORT_VPN).build())
), any(NetworkCallback.class)); ), any(NetworkCallback.class));
} }
......
...@@ -77,6 +77,8 @@ class FakeSecurityController( ...@@ -77,6 +77,8 @@ class FakeSecurityController(
override fun isVpnBranded(): Boolean = fakeState.isVpnBranded override fun isVpnBranded(): Boolean = fakeState.isVpnBranded
override fun isVpnValidated(): Boolean = fakeState.isVpnValidated
override fun getPrimaryVpnName(): String? = fakeState.primaryVpnName override fun getPrimaryVpnName(): String? = fakeState.primaryVpnName
override fun getWorkProfileVpnName(): String? = fakeState.workProfileVpnName override fun getWorkProfileVpnName(): String? = fakeState.workProfileVpnName
...@@ -110,6 +112,7 @@ class FakeSecurityController( ...@@ -110,6 +112,7 @@ class FakeSecurityController(
var isVpnEnabled: Boolean = false, var isVpnEnabled: Boolean = false,
var isVpnRestricted: Boolean = false, var isVpnRestricted: Boolean = false,
var isVpnBranded: Boolean = false, var isVpnBranded: Boolean = false,
var isVpnValidated: Boolean = false,
var primaryVpnName: String? = null, var primaryVpnName: String? = null,
var workProfileVpnName: String? = null, var workProfileVpnName: String? = null,
var hasCACertInCurrentUser: Boolean = false, var hasCACertInCurrentUser: Boolean = false,
......
...@@ -108,6 +108,11 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa ...@@ -108,6 +108,11 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa
return false; return false;
} }
@Override
public boolean isVpnValidated() {
return false;
}
@Override @Override
public String getPrimaryVpnName() { public String getPrimaryVpnName() {
return null; return null;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment