Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 47d13239 authored by Philipp Weiß's avatar Philipp Weiß Committed by android-build-merger
Browse files

Merge "Implement CACert queries in SecurityController" into oc-dev

am: 4c78ac9c

Change-Id: Ic4a8e36b7f14ad8ebb2b23e76ef6c38f1bc1da72
parents 537ece98 4c78ac9c
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -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;
+69 −4
Original line number Original line Diff line number Diff line
@@ -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();
            }
        }
    }
}
}
+83 −1
Original line number Original line Diff line number Diff line
@@ -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());
    }
}
}