diff --git a/server/Android.mk b/server/Android.mk
index 55aa87cde6838c9946e7a6a272a703aa1e02fceb..e7b01a367ca9c8b396f89180059f46112eaa77a2 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -21,7 +21,6 @@ LOCAL_C_INCLUDES := \
         bionic/libc/dns/include \
         external/libcxx/include \
         external/mdnsresponder/mDNSShared \
-        external/openssl/include \
         system/netd/include \
 
 LOCAL_CLANG := true
@@ -37,8 +36,12 @@ LOCAL_SHARED_LIBRARIES := \
         liblogwrap \
         libmdnssd \
         libnetutils \
+        libnl \
         libsysutils \
 
+LOCAL_STATIC_LIBRARIES := \
+        libpcap \
+
 LOCAL_SRC_FILES := \
         BandwidthController.cpp \
         ClatdController.cpp \
@@ -62,6 +65,7 @@ LOCAL_SRC_FILES := \
         ResolverController.cpp \
         RouteController.cpp \
         SoftapController.cpp \
+        StrictController.cpp \
         TetherController.cpp \
         UidRanges.cpp \
         VirtualNetwork.cpp \
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index e2d2308d52e758cf8bdda22fb51e5191bd1c5732..d9acce9de231030c5612bb78bcbd0c0b80ed8274 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -92,6 +92,7 @@ InterfaceController *CommandListener::sInterfaceCtrl = NULL;
 ResolverController *CommandListener::sResolverCtrl = NULL;
 FirewallController *CommandListener::sFirewallCtrl = NULL;
 ClatdController *CommandListener::sClatdCtrl = NULL;
+StrictController *CommandListener::sStrictCtrl = NULL;
 
 /**
  * List of module chains to be created, along with explicit ordering. ORDERING
@@ -116,6 +117,7 @@ static const char* FILTER_FORWARD[] = {
 static const char* FILTER_OUTPUT[] = {
         OEM_IPTABLES_FILTER_OUTPUT,
         FirewallController::LOCAL_OUTPUT,
+        StrictController::LOCAL_OUTPUT,
         BandwidthController::LOCAL_OUTPUT,
         NULL,
 };
@@ -182,6 +184,7 @@ CommandListener::CommandListener() :
     registerCmd(new FirewallCmd());
     registerCmd(new ClatdCmd());
     registerCmd(new NetworkCommand());
+    registerCmd(new StrictCmd());
 
     if (!sNetCtrl)
         sNetCtrl = new NetworkController();
@@ -205,6 +208,8 @@ CommandListener::CommandListener() :
         sInterfaceCtrl = new InterfaceController();
     if (!sClatdCtrl)
         sClatdCtrl = new ClatdController(sNetCtrl);
+    if (!sStrictCtrl)
+        sStrictCtrl = new StrictController();
 
     /*
      * This is the only time we touch top-level chains in iptables; controllers
@@ -1353,6 +1358,76 @@ int CommandListener::ClatdCmd::runCommand(SocketClient *cli, int argc,
     return 0;
 }
 
+CommandListener::StrictCmd::StrictCmd() :
+    NetdCommand("strict") {
+}
+
+int CommandListener::StrictCmd::sendGenericOkFail(SocketClient *cli, int cond) {
+    if (!cond) {
+        cli->sendMsg(ResponseCode::CommandOkay, "Strict command succeeded", false);
+    } else {
+        cli->sendMsg(ResponseCode::OperationFailed, "Strict command failed", false);
+    }
+    return 0;
+}
+
+StrictPenalty CommandListener::StrictCmd::parsePenalty(const char* arg) {
+    if (!strcmp(arg, "reject")) {
+        return REJECT;
+    } else if (!strcmp(arg, "log")) {
+        return LOG;
+    } else if (!strcmp(arg, "accept")) {
+        return ACCEPT;
+    } else {
+        return INVALID;
+    }
+}
+
+int CommandListener::StrictCmd::runCommand(SocketClient *cli, int argc,
+        char **argv) {
+    if (argc < 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing command", false);
+        return 0;
+    }
+
+    if (!strcmp(argv[1], "enable")) {
+        int res = sStrictCtrl->enableStrict();
+        return sendGenericOkFail(cli, res);
+    }
+    if (!strcmp(argv[1], "disable")) {
+        int res = sStrictCtrl->disableStrict();
+        return sendGenericOkFail(cli, res);
+    }
+
+    if (!strcmp(argv[1], "set_uid_cleartext_policy")) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: strict set_uid_cleartext_policy <uid> <accept|log|reject>",
+                         false);
+            return 0;
+        }
+
+        errno = 0;
+        unsigned long int uid = strtoul(argv[2], NULL, 0);
+        if (errno || uid > UID_MAX) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid UID", false);
+            return 0;
+        }
+
+        StrictPenalty penalty = parsePenalty(argv[3]);
+        if (penalty == INVALID) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid penalty argument", false);
+            return 0;
+        }
+
+        int res = sStrictCtrl->setUidCleartextPenalty((uid_t) uid, penalty);
+        return sendGenericOkFail(cli, res);
+    }
+
+    cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false);
+    return 0;
+}
+
 CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") {
 }
 
diff --git a/server/CommandListener.h b/server/CommandListener.h
index f60564756570718deafa0bc08f251f25286b1304..b71bc45e2760fae9a1b411ae60aeeab3886b89cf 100644
--- a/server/CommandListener.h
+++ b/server/CommandListener.h
@@ -31,6 +31,7 @@
 #include "ResolverController.h"
 #include "FirewallController.h"
 #include "ClatdController.h"
+#include "StrictController.h"
 
 class CommandListener : public FrameworkListener {
     static TetherController *sTetherCtrl;
@@ -43,6 +44,7 @@ class CommandListener : public FrameworkListener {
     static ResolverController *sResolverCtrl;
     static FirewallController *sFirewallCtrl;
     static ClatdController *sClatdCtrl;
+    static StrictController *sStrictCtrl;
 
 public:
     static NetworkController *sNetCtrl;
@@ -143,6 +145,16 @@ private:
         int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
+    class StrictCmd : public NetdCommand {
+    public:
+        StrictCmd();
+        virtual ~StrictCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    protected:
+        int sendGenericOkFail(SocketClient *cli, int cond);
+        static StrictPenalty parsePenalty(const char* arg);
+    };
+
     class NetworkCommand : public NetdCommand {
     public:
         NetworkCommand();
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp
index 6c81c18b69008eccf9a865a64a5fd7d0fc5105d0..0a5a3f02a1956f271963a0d249bbc1d506a0aad5 100644
--- a/server/NetlinkHandler.cpp
+++ b/server/NetlinkHandler.cpp
@@ -109,6 +109,11 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) {
         const char *iface = evt->findParam("INTERFACE");
         notifyQuotaLimitReached(alertName, iface);
 
+    } else if (!strcmp(subsys, "strict")) {
+        const char *uid = evt->findParam("UID");
+        const char *hex = evt->findParam("HEX");
+        notifyStrictCleartext(uid, hex);
+
     } else if (!strcmp(subsys, "xt_idletimer")) {
         const char *label = evt->findParam("INTERFACE");
         const char *state = evt->findParam("STATE");
@@ -196,3 +201,7 @@ void NetlinkHandler::notifyRouteChange(int action, const char *route,
            *iface ? " dev " : "",
            iface);
 }
+
+void NetlinkHandler::notifyStrictCleartext(const char* uid, const char* hex) {
+    notify(ResponseCode::StrictCleartext, "%s %s", uid, hex);
+}
diff --git a/server/NetlinkHandler.h b/server/NetlinkHandler.h
index 83baa2b8834554b255495d5ce7ed654f75b4ded1..bee52dcb9ed3b0da9d92e6079bbe46500dc285a9 100644
--- a/server/NetlinkHandler.h
+++ b/server/NetlinkHandler.h
@@ -46,5 +46,6 @@ protected:
     void notifyInterfaceDnsServers(const char *iface, const char *lifetime,
                                    const char *servers);
     void notifyRouteChange(int action, const char *route, const char *gateway, const char *iface);
+    void notifyStrictCleartext(const char* uid, const char* hex);
 };
 #endif
diff --git a/server/NetlinkManager.cpp b/server/NetlinkManager.cpp
index 1d731acb8de7eaa51ef61a10e4227c7b87bb33fd..79b00ee3e41040dc2b62a3e1d5b4e87680dcadf5 100644
--- a/server/NetlinkManager.cpp
+++ b/server/NetlinkManager.cpp
@@ -30,10 +30,24 @@
 
 #include <cutils/log.h>
 
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_log.h>
+#include <linux/netfilter/nfnetlink_compat.h>
+
+#include <arpa/inet.h>
+
 #include "NetlinkManager.h"
 #include "NetlinkHandler.h"
 
+#include "pcap-netfilter-linux-android.h"
+
 const int NetlinkManager::NFLOG_QUOTA_GROUP = 1;
+const int NetlinkManager::NETFILTER_STRICT_GROUP = 2;
 
 NetlinkManager *NetlinkManager::sInstance = NULL;
 
@@ -112,11 +126,27 @@ int NetlinkManager::start() {
     }
 
     if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
-        NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
-        ALOGE("Unable to open quota2 logging socket");
+            NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
+        ALOGE("Unable to open quota socket");
         // TODO: return -1 once the emulator gets a new kernel.
     }
 
+    if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,
+            0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST)) == NULL) {
+        ALOGE("Unable to open strict socket");
+        // TODO: return -1 once the emulator gets a new kernel.
+    } else {
+        if (android_nflog_send_config_cmd(mStrictSock, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) {
+            ALOGE("Failed NFULNL_CFG_CMD_PF_UNBIND: %s", strerror(errno));
+        }
+        if (android_nflog_send_config_cmd(mStrictSock, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) {
+            ALOGE("Failed NFULNL_CFG_CMD_PF_BIND: %s", strerror(errno));
+        }
+        if (android_nflog_send_config_cmd(mStrictSock, 0, NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) {
+            ALOGE("Failed NFULNL_CFG_CMD_BIND: %s", strerror(errno));
+        }
+    }
+
     return 0;
 }
 
@@ -158,5 +188,18 @@ int NetlinkManager::stop() {
         mQuotaSock = -1;
     }
 
+    if (mStrictHandler) {
+        if (mStrictHandler->stop()) {
+            ALOGE("Unable to stop strict NetlinkHandler: %s", strerror(errno));
+            status = -1;
+        }
+
+        delete mStrictHandler;
+        mStrictHandler = NULL;
+
+        close(mStrictSock);
+        mStrictSock = -1;
+    }
+
     return status;
 }
diff --git a/server/NetlinkManager.h b/server/NetlinkManager.h
index 5187a59a514a08260c3733439119b3745f76edee..2bfaee922594652eb3555c125001fb69663d0d2b 100644
--- a/server/NetlinkManager.h
+++ b/server/NetlinkManager.h
@@ -32,9 +32,11 @@ private:
     NetlinkHandler       *mUeventHandler;
     NetlinkHandler       *mRouteHandler;
     NetlinkHandler       *mQuotaHandler;
+    NetlinkHandler       *mStrictHandler;
     int                  mUeventSock;
     int                  mRouteSock;
     int                  mQuotaSock;
+    int                  mStrictSock;
 
 public:
     virtual ~NetlinkManager();
@@ -47,11 +49,10 @@ public:
 
     static NetlinkManager *Instance();
 
-    /* This is the nflog group arg that the xt_quota2 neftiler will use. */
+    /* Group used by xt_quota2 */
     static const int NFLOG_QUOTA_GROUP;
-
-    /* This is the group that the xt_IDLETIMER netfilter will use. */
-    static const int IDLETIMER_GROUP;
+    /* Group used by StrictController rules */
+    static const int NETFILTER_STRICT_GROUP;
 
 private:
     NetlinkManager();
diff --git a/server/ResponseCode.h b/server/ResponseCode.h
index 6a0c22c3364d9904b214eaa45866c262bdeef8df..19d76c38b55bf7ae54c9f3b9b5bff5c9b756ceaa 100644
--- a/server/ResponseCode.h
+++ b/server/ResponseCode.h
@@ -77,5 +77,6 @@ public:
     static const int InterfaceAddressChange         = 614;
     static const int InterfaceDnsInfo               = 615;
     static const int RouteChange                    = 616;
+    static const int StrictCleartext                = 617;
 };
 #endif
diff --git a/server/StrictController.cpp b/server/StrictController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..20232ea7a4adadd38130ea6e16ac973483858f31
--- /dev/null
+++ b/server/StrictController.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "StrictController"
+#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+
+#include "NetdConstants.h"
+#include "StrictController.h"
+
+const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT";
+const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect";
+const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught";
+const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log";
+const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject";
+
+StrictController::StrictController(void) {
+}
+
+int StrictController::enableStrict(void) {
+    int res = 0;
+
+    disableStrict();
+
+    // Mark 0x01 means resolved and ACCEPT
+    // Mark 0x02 means resolved and REJECT
+
+    // Chain triggered when cleartext socket detected and penalty is log
+    res |= execIptables(V4V6, "-N", LOCAL_PENALTY_LOG, NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG,
+            "-j", "CONNMARK", "--or-mark", "0x01000000", NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG,
+            "-j", "NFLOG", "--nflog-group", "0", NULL);
+
+    // Chain triggered when cleartext socket detected and penalty is reject
+    res |= execIptables(V4V6, "-N", LOCAL_PENALTY_REJECT, NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
+            "-j", "CONNMARK", "--or-mark", "0x02000000", NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
+            "-j", "NFLOG", "--nflog-group", "0", NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
+            "-j", "REJECT", NULL);
+
+    // Create chain to detect non-TLS traffic. We use a high-order
+    // mark bit to keep track of connections that we've already resolved.
+    res |= execIptables(V4V6, "-N", LOCAL_CLEAR_DETECT, NULL);
+    res |= execIptables(V4V6, "-N", LOCAL_CLEAR_CAUGHT, NULL);
+
+    // Quickly skip connections that we've already resolved
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+            "-m", "connmark", "--mark", "0x02000000/0x02000000",
+            "-j", "REJECT", NULL);
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+            "-m", "connmark", "--mark", "0x01000000/0x01000000",
+            "-j", "RETURN", NULL);
+
+    // Look for IPv4 TCP/UDP connections with TLS/DTLS header
+    res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000 &&"
+                                  "0>>22&0x3C@ 12>>26&0x3C@ 4&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", "0x01000000", NULL);
+    res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
+            "-m", "u32", "--u32", "0>>22&0x3C@ 8&0xFFFF0000=0x16FE0000 &&"
+                                  "0>>22&0x3C@ 20&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", "0x01000000", NULL);
+
+    // Look for IPv6 TCP/UDP connections with TLS/DTLS header.  The IPv6 header
+    // doesn't have an IHL field to shift with, so we have to manually add in
+    // the 40-byte offset at every step.
+    res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "u32", "--u32", "52>>26&0x3C@ 40&0xFFFF0000=0x16030000 &&"
+                                  "52>>26&0x3C@ 44&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", "0x01000000", NULL);
+    res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
+            "-m", "u32", "--u32", "48&0xFFFF0000=0x16FE0000 &&"
+                                  "60&0x00FF0000=0x00010000",
+            "-j", "CONNMARK", "--or-mark", "0x01000000", NULL);
+
+    // Skip newly classified connections from above
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+            "-m", "connmark", "--mark", "0x01000000/0x01000000",
+            "-j", "RETURN", NULL);
+
+    // Handle TCP/UDP payloads that didn't match TLS/DTLS filters above,
+    // which means we've probably found cleartext data.  The TCP variant
+    // depends on u32 returning false when we try reading into the message
+    // body to ignore empty ACK packets.
+    res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "state", "--state", "ESTABLISHED",
+            "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0x0=0x0",
+            "-j", LOCAL_CLEAR_CAUGHT, NULL);
+    res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
+            "-m", "state", "--state", "ESTABLISHED",
+            "-m", "u32", "--u32", "52>>26&0x3C@ 40&0x0=0x0",
+            "-j", LOCAL_CLEAR_CAUGHT, NULL);
+
+    res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
+            "-j", LOCAL_CLEAR_CAUGHT, NULL);
+
+    return res;
+}
+
+int StrictController::disableStrict(void) {
+    int res = 0;
+
+    // Flush any existing rules
+    res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
+
+    res |= execIptables(V4V6, "-F", LOCAL_PENALTY_LOG, NULL);
+    res |= execIptables(V4V6, "-F", LOCAL_PENALTY_REJECT, NULL);
+    res |= execIptables(V4V6, "-F", LOCAL_CLEAR_CAUGHT, NULL);
+    res |= execIptables(V4V6, "-F", LOCAL_CLEAR_DETECT, NULL);
+
+    res |= execIptables(V4V6, "-X", LOCAL_PENALTY_LOG, NULL);
+    res |= execIptables(V4V6, "-X", LOCAL_PENALTY_REJECT, NULL);
+    res |= execIptables(V4V6, "-X", LOCAL_CLEAR_CAUGHT, NULL);
+    res |= execIptables(V4V6, "-X", LOCAL_CLEAR_DETECT, NULL);
+
+    return res;
+}
+
+int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) {
+    char uidStr[16];
+    sprintf(uidStr, "%d", uid);
+
+    int res = 0;
+    if (penalty == ACCEPT) {
+        // Clean up any old rules
+        execIptables(V4V6, "-D", LOCAL_OUTPUT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_CLEAR_DETECT, NULL);
+        execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_PENALTY_LOG, NULL);
+        execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_PENALTY_REJECT, NULL);
+
+    } else {
+        // Always take a detour to investigate this UID
+        res |= execIptables(V4V6, "-I", LOCAL_OUTPUT,
+                "-m", "owner", "--uid-owner", uidStr,
+                "-j", LOCAL_CLEAR_DETECT, NULL);
+
+        if (penalty == LOG) {
+            res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
+                    "-m", "owner", "--uid-owner", uidStr,
+                    "-j", LOCAL_PENALTY_LOG, NULL);
+        } else if (penalty == REJECT) {
+            res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
+                    "-m", "owner", "--uid-owner", uidStr,
+                    "-j", LOCAL_PENALTY_REJECT, NULL);
+        }
+    }
+
+    return res;
+}
diff --git a/server/StrictController.h b/server/StrictController.h
new file mode 100644
index 0000000000000000000000000000000000000000..52a67794027e93c7aaafeffc8d07358d5ed51d86
--- /dev/null
+++ b/server/StrictController.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef _STRICT_CONTROLLER_H
+#define _STRICT_CONTROLLER_H
+
+#include <string>
+
+enum StrictPenalty { INVALID, ACCEPT, LOG, REJECT };
+
+/*
+ * Help apps catch unwanted low-level networking behavior, like
+ * connections not wrapped in TLS.
+ */
+class StrictController {
+public:
+    StrictController();
+
+    int enableStrict(void);
+    int disableStrict(void);
+
+    int setUidCleartextPenalty(uid_t, StrictPenalty);
+
+    static const char* LOCAL_OUTPUT;
+    static const char* LOCAL_CLEAR_DETECT;
+    static const char* LOCAL_CLEAR_CAUGHT;
+    static const char* LOCAL_PENALTY_LOG;
+    static const char* LOCAL_PENALTY_REJECT;
+};
+
+#endif