Merge "Implement CACert queries in SecurityController" into oc-dev
This commit is contained in:
@@ -599,7 +599,7 @@ public final class KeyChain {
|
|||||||
private final Context context;
|
private final Context context;
|
||||||
private final ServiceConnection serviceConnection;
|
private final ServiceConnection serviceConnection;
|
||||||
private final IKeyChainService service;
|
private final IKeyChainService service;
|
||||||
private KeyChainConnection(Context context,
|
protected KeyChainConnection(Context context,
|
||||||
ServiceConnection serviceConnection,
|
ServiceConnection serviceConnection,
|
||||||
IKeyChainService service) {
|
IKeyChainService service) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ package com.android.systemui.statusbar.policy;
|
|||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
@@ -28,16 +31,23 @@ import android.net.IConnectivityManager;
|
|||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
import android.security.KeyChain;
|
||||||
|
import android.security.KeyChain.KeyChainConnection;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.net.LegacyVpnInfo;
|
import com.android.internal.net.LegacyVpnInfo;
|
||||||
import com.android.internal.net.VpnConfig;
|
import com.android.internal.net.VpnConfig;
|
||||||
|
import com.android.systemui.Dependency;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.settings.CurrentUserTracker;
|
import com.android.systemui.settings.CurrentUserTracker;
|
||||||
|
|
||||||
@@ -59,6 +69,8 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
|
|||||||
|
|
||||||
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";
|
||||||
|
|
||||||
|
private static final int CA_CERT_LOADING_RETRY_TIME_IN_MS = 30_000;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ConnectivityManager mConnectivityManager;
|
private final ConnectivityManager mConnectivityManager;
|
||||||
private final IConnectivityManager mConnectivityManagerService;
|
private final IConnectivityManager mConnectivityManagerService;
|
||||||
@@ -73,6 +85,10 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
|
|||||||
private int mCurrentUserId;
|
private int mCurrentUserId;
|
||||||
private int mVpnUserId;
|
private int mVpnUserId;
|
||||||
|
|
||||||
|
// Key: userId, Value: whether the user has CACerts installed
|
||||||
|
// Needs to be cached here since the query has to be asynchronous
|
||||||
|
private ArrayMap<Integer, Boolean> mHasCACerts = new ArrayMap<Integer, Boolean>();
|
||||||
|
|
||||||
public SecurityControllerImpl(Context context) {
|
public SecurityControllerImpl(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
@@ -86,6 +102,11 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
|
|||||||
mUserManager = (UserManager)
|
mUserManager = (UserManager)
|
||||||
context.getSystemService(Context.USER_SERVICE);
|
context.getSystemService(Context.USER_SERVICE);
|
||||||
|
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
|
||||||
|
context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null,
|
||||||
|
new Handler(Dependency.get(Dependency.BG_LOOPER)));
|
||||||
|
|
||||||
// TODO: re-register network callback on user change.
|
// TODO: re-register network callback on user change.
|
||||||
mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
|
mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
|
||||||
onUserSwitched(ActivityManager.getCurrentUser());
|
onUserSwitched(ActivityManager.getCurrentUser());
|
||||||
@@ -218,14 +239,16 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCACertInCurrentUser() {
|
public boolean hasCACertInCurrentUser() {
|
||||||
//TODO: implement
|
Boolean hasCACerts = mHasCACerts.get(mCurrentUserId);
|
||||||
return false;
|
return hasCACerts != null && hasCACerts.booleanValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCACertInWorkProfile() {
|
public boolean hasCACertInWorkProfile() {
|
||||||
//TODO: implement
|
int userId = getWorkProfileUserId(mCurrentUserId);
|
||||||
return false;
|
if (userId == UserHandle.USER_NULL) return false;
|
||||||
|
Boolean hasCACerts = mHasCACerts.get(userId);
|
||||||
|
return hasCACerts != null && hasCACerts.booleanValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -256,9 +279,16 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
|
|||||||
} else {
|
} else {
|
||||||
mVpnUserId = mCurrentUserId;
|
mVpnUserId = mCurrentUserId;
|
||||||
}
|
}
|
||||||
|
refreshCACerts();
|
||||||
fireCallbacks();
|
fireCallbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshCACerts() {
|
||||||
|
new CACertLoader().execute(mCurrentUserId);
|
||||||
|
int workProfileId = getWorkProfileUserId(mCurrentUserId);
|
||||||
|
if (workProfileId != UserHandle.USER_NULL) new CACertLoader().execute(workProfileId);
|
||||||
|
}
|
||||||
|
|
||||||
private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
|
private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
|
||||||
if (cfg.legacy) {
|
if (cfg.legacy) {
|
||||||
return mContext.getString(R.string.legacy_vpn_name);
|
return mContext.getString(R.string.legacy_vpn_name);
|
||||||
@@ -348,4 +378,39 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
|
|||||||
fireCallbacks();
|
fireCallbacks();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||||
|
@Override public void onReceive(Context context, Intent intent) {
|
||||||
|
if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
|
||||||
|
refreshCACerts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected class CACertLoader extends AsyncTask<Integer, Void, Pair<Integer, Boolean> > {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pair<Integer, Boolean> doInBackground(Integer... userId) {
|
||||||
|
try (KeyChainConnection conn = KeyChain.bindAsUser(mContext,
|
||||||
|
UserHandle.of(userId[0]))) {
|
||||||
|
boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
|
||||||
|
return new Pair<Integer, Boolean>(userId[0], hasCACerts);
|
||||||
|
} catch (RemoteException | InterruptedException | AssertionError e) {
|
||||||
|
Log.i(TAG, e.getMessage());
|
||||||
|
new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
|
||||||
|
() -> new CACertLoader().execute(userId[0]),
|
||||||
|
CA_CERT_LOADING_RETRY_TIME_IN_MS);
|
||||||
|
return new Pair<Integer, Boolean>(userId[0], null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Pair<Integer, Boolean> result) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onPostExecute " + result);
|
||||||
|
if (result.second != null) {
|
||||||
|
mHasCACerts.put(result.first, result.second);
|
||||||
|
fireCallbacks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,31 +21,76 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.StringParceledListSlice;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
import android.security.IKeyChainService;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class SecurityControllerTest extends SysuiTestCase {
|
public class SecurityControllerTest extends SysuiTestCase implements SecurityControllerCallback {
|
||||||
private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
|
private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
|
||||||
|
private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class);
|
||||||
private SecurityControllerImpl mSecurityController;
|
private SecurityControllerImpl mSecurityController;
|
||||||
|
private CountDownLatch mStateChangedLatch;
|
||||||
|
|
||||||
|
// implementing SecurityControllerCallback
|
||||||
|
@Override
|
||||||
|
public void onStateChanged() {
|
||||||
|
mStateChangedLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
|
mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
|
||||||
mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class));
|
mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class));
|
||||||
|
|
||||||
|
Intent intent = new Intent(IKeyChainService.class.getName());
|
||||||
|
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
|
||||||
|
mContext.addMockService(comp, mKeyChainService);
|
||||||
|
|
||||||
|
when(mKeyChainService.getUserCaAliases())
|
||||||
|
.thenReturn(new StringParceledListSlice(new ArrayList<String>()));
|
||||||
|
// Without this line, mKeyChainService gets wrapped in a proxy when Stub.asInterface() is
|
||||||
|
// used on it, and the mocking above does not work.
|
||||||
|
when(mKeyChainService.queryLocalInterface("android.security.IKeyChainService"))
|
||||||
|
.thenReturn(mKeyChainService);
|
||||||
|
|
||||||
mSecurityController = new SecurityControllerImpl(mContext);
|
mSecurityController = new SecurityControllerImpl(mContext);
|
||||||
|
|
||||||
|
// Wait for one or two state changes from the CACertLoader(s) in the constructor of
|
||||||
|
// mSecurityController
|
||||||
|
mStateChangedLatch = new CountDownLatch(mSecurityController.hasWorkProfile() ? 2 : 1);
|
||||||
|
mSecurityController.addCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
mSecurityController.removeCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -62,4 +107,41 @@ public class SecurityControllerTest extends SysuiTestCase {
|
|||||||
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn("organization");
|
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn("organization");
|
||||||
assertEquals("organization", mSecurityController.getDeviceOwnerOrganizationName());
|
assertEquals("organization", mSecurityController.getDeviceOwnerOrganizationName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore("Flaky")
|
||||||
|
public void testCaCertLoader() throws Exception {
|
||||||
|
assertTrue(mStateChangedLatch.await(3, TimeUnit.SECONDS));
|
||||||
|
assertFalse(mSecurityController.hasCACertInCurrentUser());
|
||||||
|
|
||||||
|
// With a CA cert
|
||||||
|
|
||||||
|
mStateChangedLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
when(mKeyChainService.getUserCaAliases())
|
||||||
|
.thenReturn(new StringParceledListSlice(Arrays.asList("One CA Alias")));
|
||||||
|
|
||||||
|
mSecurityController.new CACertLoader()
|
||||||
|
.execute(0);
|
||||||
|
|
||||||
|
assertTrue(mStateChangedLatch.await(3, TimeUnit.SECONDS));
|
||||||
|
assertTrue(mSecurityController.hasCACertInCurrentUser());
|
||||||
|
|
||||||
|
// Exception
|
||||||
|
|
||||||
|
mStateChangedLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
when(mKeyChainService.getUserCaAliases())
|
||||||
|
.thenThrow(new AssertionError("Test AssertionError"))
|
||||||
|
.thenReturn(new StringParceledListSlice(new ArrayList<String>()));
|
||||||
|
|
||||||
|
mSecurityController.new CACertLoader()
|
||||||
|
.execute(0);
|
||||||
|
|
||||||
|
assertFalse(mStateChangedLatch.await(3, TimeUnit.SECONDS));
|
||||||
|
assertTrue(mSecurityController.hasCACertInCurrentUser());
|
||||||
|
// The retry takes 30s
|
||||||
|
//assertTrue(mStateChangedLatch.await(31, TimeUnit.SECONDS));
|
||||||
|
//assertFalse(mSecurityController.hasCACertInCurrentUser());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user