NetworkManagement SocketTagger: Migrate QTagUid support to JNI.

* Instead of javaland trying to write commands to
   /proc/net/xt_qtaguid/ctrl
use the libcutils/qtaguid.c support via JNI.
* Get rid of tagToKernel() handled by qtaguid library.

Requires libcutils changes from c/132538/

Change-Id: I9de5b3fa4a596c56835024c6d376769a0eea7db1
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 9f6ab31..8445ad1 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -75,30 +75,20 @@
             Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
                     + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
         }
-        try {
-            // TODO: skip tagging when options would be no-op
-            tagSocketFd(fd, options.statsTag, options.statsUid);
-        } catch (IOException e) {
-            throw new SocketException("Problem tagging socket", e);
-        }
+        // TODO: skip tagging when options would be no-op
+        tagSocketFd(fd, options.statsTag, options.statsUid);
     }
 
-    private void tagSocketFd(FileDescriptor fd, int tag, int uid) throws IOException {
-        final int fdNum = fd.getInt$();
-        if (fdNum == -1 || (tag == -1 && uid == -1)) return;
+    private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
+        int errno;
+        if (tag == -1 && uid == -1) return;
 
-        String cmd = "t " + fdNum;
-        if (tag == -1) {
-            // Case where just the uid needs adjusting. But probably the caller
-            // will want to track his own name here, just in case.
-            cmd += " 0";
-        } else {
-            cmd += " " + tagToKernel(tag);
+        errno = native_tagSocketFd(fd, tag, uid);
+        if (errno < 0) {
+            Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
+                  + tag + ", " +
+                  + uid + ") failed with errno" + errno);
         }
-        if (uid != -1) {
-            cmd += " " + uid;
-        }
-        internalModuleCtrl(cmd);
     }
 
     @Override
@@ -106,19 +96,18 @@
         if (LOGD) {
             Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
         }
-        try {
-            unTagSocketFd(fd);
-        } catch (IOException e) {
-            throw new SocketException("Problem untagging socket", e);
-        }
+        unTagSocketFd(fd);
     }
 
-    private void unTagSocketFd(FileDescriptor fd) throws IOException {
-        int fdNum = fd.getInt$();
+    private void unTagSocketFd(FileDescriptor fd) {
         final SocketTags options = threadSocketTags.get();
-        if (fdNum == -1 || (options.statsTag == -1 && options.statsUid == -1)) return;
-        String cmd = "u " + fdNum;
-        internalModuleCtrl(cmd);
+        int errno;
+        if (options.statsTag == -1 && options.statsUid == -1) return;
+
+        errno = native_untagSocketFd(fd);
+        if (errno < 0) {
+            Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
+        }
     }
 
     public static class SocketTags {
@@ -127,69 +116,20 @@
     }
 
     public static void setKernelCounterSet(int uid, int counterSet) {
-        final StringBuilder command = new StringBuilder();
-        command.append("s ").append(counterSet).append(" ").append(uid);
-        try {
-            internalModuleCtrl(command.toString());
-        } catch (IOException e) {
-            Slog.w(TAG, "problem changing counter set for uid " + uid + " : " + e);
+        int errno = native_setCounterSet(counterSet, uid);
+        if (errno < 0) {
+            Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " + errno);
         }
     }
 
     public static void resetKernelUidStats(int uid) {
-        final StringBuilder command = new StringBuilder();
-        command.append("d 0 ").append(uid);
-        try {
-            internalModuleCtrl(command.toString());
-        } catch (IOException e) {
-            Slog.w(TAG, "problem clearing counters for uid " + uid + " : " + e);
+        int errno = native_deleteTagData(0, uid);
+        if (errno < 0) {
+            Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
         }
     }
 
     /**
-     * Sends commands to the kernel netfilter module.
-     *
-     * @param cmd command string for the qtaguid netfilter module. May not be null.
-     *   <p>Supports:
-     *     <ul><li>tag a socket:<br>
-     *        <code>t <i>sock_fd</i> <i>acct_tag</i> [<i>uid_in_case_caller_is_acting_on_behalf_of</i>]</code><br>
-     *     <code>*_tag</code> defaults to default_policy_tag_from_uid(uid_of_caller)<br>
-     *     <code>acct_tag</code> is either 0 or greater that 2^32.<br>
-     *     <code>uid_*</code> is only settable by privileged UIDs (DownloadManager,...)
-     *     </li>
-     *     <li>untag a socket, preserving counters:<br>
-     *       <code>u <i>sock_fd</i></code>
-     *     </li></ul>
-     *   <p>Notes:<br>
-     *   <ul><li><i>sock_fd</i> is withing the callers process space.</li>
-     *   <li><i>*_tag</i> are 64bit values</li></ul>
-     *
-     */
-    private static void internalModuleCtrl(String cmd) throws IOException {
-        if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
-
-        // TODO: migrate to native library for tagging commands
-        FileOutputStream procOut = null;
-        try {
-            procOut = new FileOutputStream("/proc/net/xt_qtaguid/ctrl");
-            procOut.write(cmd.getBytes(Charsets.US_ASCII));
-        } finally {
-            IoUtils.closeQuietly(procOut);
-        }
-    }
-
-    /**
-     * Convert {@link Integer} tag to {@code /proc/} format. Assumes unsigned
-     * base-10 format like {@code 2147483647}. Currently strips signed bit to
-     * avoid using {@link BigInteger}.
-     */
-    public static String tagToKernel(int tag) {
-        // TODO: eventually write in hex, since that's what proc exports
-        // TODO: migrate to direct integer instead of odd shifting
-        return Long.toString((((long) tag) << 32) & 0x7FFFFFFF00000000L);
-    }
-
-    /**
      * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
      * format like {@code 0x7fffffff00000000}.
      */
@@ -197,4 +137,9 @@
         // TODO: migrate to direct integer instead of odd shifting
         return (int) (Long.decode(string) >> 32);
     }
+
+    private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
+    private static native int native_untagSocketFd(FileDescriptor fd);
+    private static native int native_setCounterSet(int uid, int counterSetNum);
+    private static native int native_deleteTagData(int tag, int uid);
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index aece5f0..59a03e7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -143,6 +143,7 @@
 	android_server_BluetoothService.cpp \
 	android_server_BluetoothEventLoop.cpp \
 	android_server_BluetoothA2dpService.cpp \
+	android_server_NetworkManagementSocketTagger.cpp \
 	android_server_Watchdog.cpp \
 	android_ddm_DdmHandleNativeHeap.cpp \
 	com_android_internal_os_ZygoteInit.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index dd7dd86..12ec1b6 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -155,6 +155,7 @@
 extern int register_android_server_BluetoothService(JNIEnv* env);
 extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
 extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
+extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
 extern int register_android_server_Watchdog(JNIEnv* env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
@@ -1178,6 +1179,7 @@
     REG_JNI(register_android_server_BluetoothService),
     REG_JNI(register_android_server_BluetoothEventLoop),
     REG_JNI(register_android_server_BluetoothA2dpService),
+    REG_JNI(register_android_server_NetworkManagementSocketTagger),
     REG_JNI(register_android_server_Watchdog),
     REG_JNI(register_android_ddm_DdmHandleNativeHeap),
     REG_JNI(register_android_backup_BackupDataInput),
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
new file mode 100644
index 0000000..c279ced
--- /dev/null
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011, 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.
+ */
+
+#define LOG_TAG "NMST_QTagUidNative"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+
+#include "jni.h"
+#include <utils/misc.h>
+#include <cutils/qtaguid.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+namespace android {
+
+static jint QTagUid_tagSocketFd(JNIEnv* env, jclass,
+                                jobject fileDescriptor,
+                                jint tagNum, jint uid) {
+  int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+
+  if (env->ExceptionOccurred() != NULL) {
+    LOGE("Can't get FileDescriptor num");
+    return (jint)-1;
+  }
+
+  int res = qtaguid_tagSocket(userFd, tagNum, uid);
+  if (res < 0) {
+    return (jint)-errno;
+  }
+  return (jint)res;
+}
+
+static int QTagUid_untagSocketFd(JNIEnv* env, jclass,
+                                 jobject fileDescriptor) {
+  int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+
+  if (env->ExceptionOccurred() != NULL) {
+    LOGE("Can't get FileDescriptor num");
+    return (jint)-1;
+  }
+
+  int res = qtaguid_untagSocket(userFd);
+  if (res < 0) {
+    return (jint)-errno;
+  }
+  return (jint)res;
+}
+
+static jint QTagUid_setCounterSet(JNIEnv* env, jclass,
+                                  jint setNum, jint uid) {
+
+  int res = qtaguid_setCounterSet(setNum, uid);
+  if (res < 0) {
+    return (jint)-errno;
+  }
+  return (jint)res;
+}
+
+static jint QTagUid_deleteTagData(JNIEnv* env, jclass,
+                                  jint tagNum, jint uid) {
+
+  int res = qtaguid_deleteTagData(tagNum, uid);
+  if (res < 0) {
+    return (jint)-errno;
+  }
+  return (jint)res;
+}
+
+static JNINativeMethod gQTagUidMethods[] = {
+  { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)QTagUid_tagSocketFd},
+  { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)QTagUid_untagSocketFd},
+  { "native_setCounterSet", "(II)I", (void*)QTagUid_setCounterSet},
+  { "native_deleteTagData", "(II)I", (void*)QTagUid_deleteTagData},
+};
+
+int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) {
+  return jniRegisterNativeMethods(env, "com/android/server/NetworkManagementSocketTagger", gQTagUidMethods, NELEM(gQTagUidMethods));
+}
+
+};