Add metric wrapper class for managed device metrics.

CTS test will be added in another CL.

Bug: 119906490
Test: None
Change-Id: I06e382ee68904d2315c58043a488c55f1484f7d1
diff --git a/Android.bp b/Android.bp
index 7e038ce8..c5ec161 100644
--- a/Android.bp
+++ b/Android.bp
@@ -725,6 +725,7 @@
         "android.hardware.radio-V1.3-java",
         "android.hardware.usb.gadget-V1.0-java",
         "netd_aidl_interface-java",
+        "devicepolicyprotosnano",
     ],
 
     // Loaded with System.loadLibrary by android.view.textclassifier
diff --git a/core/java/android/app/admin/DevicePolicyEventLogger.java b/core/java/android/app/admin/DevicePolicyEventLogger.java
new file mode 100644
index 0000000..f39a5f4
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyEventLogger.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package android.app.admin;
+
+import android.stats.devicepolicy.nano.StringList;
+import android.util.StatsLog;
+
+import com.android.framework.protobuf.nano.MessageNano;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+/**
+ * A wrapper for logging managed device events using {@link StatsLog}.
+ * <p/>
+ * This class allows chaining - each of its methods returns a reference to the current instance.
+ * <p/>
+ * Example usage:
+ * <code><pre>
+ * import android.stats.devicepolicy.DevicePolicyEnums;
+ *
+ * DevicePolicyEventLogger
+ *     .createEvent(DevicePolicyEnums.USER_RESTRICTION_CHANGED)
+ *     .setAdminPackageName(who)
+ *     .setString(key)
+ *     .setBoolean(enabledFromThisOwner)
+ *     .write();
+ * </pre></code>
+ *
+ * @see StatsLog
+ * @hide
+ */
+public final class DevicePolicyEventLogger {
+    private final int mEventId;
+    private int mIntValue;
+    private boolean mBooleanValue;
+    private long mTimePeriodMs;
+    private String[] mStringArrayValue;
+    private String mAdminPackageName;
+
+    private DevicePolicyEventLogger(int eventId) {
+        mEventId = eventId;
+    }
+
+    /**
+     * Creates a new {@link DevicePolicyEventLogger} instance for the specified
+     * <code>eventId</code>.
+     *
+     * @param eventId one of {@link android.stats.devicepolicy.DevicePolicyEnums} as defined in
+     * <code>core/proto/android/stats/devicepolicy/device_policy_enums.proto</code>
+     */
+    public static DevicePolicyEventLogger createEvent(int eventId) {
+        return new DevicePolicyEventLogger(eventId);
+    }
+
+    /**
+     * Returns the event id.
+     */
+    @VisibleForTesting
+    public int getEventId() {
+        return mEventId;
+    }
+
+    /**
+     * Sets a generic <code>int</code> value.
+     */
+    public DevicePolicyEventLogger setInt(int value) {
+        mIntValue = value;
+        return this;
+    }
+
+    /**
+     * Returns the generic <code>int</code> value.
+     */
+    @VisibleForTesting
+    public int getInt() {
+        return mIntValue;
+    }
+
+    /**
+     * Sets a generic <code>boolean</code> value.
+     */
+    public DevicePolicyEventLogger setBoolean(boolean value) {
+        mBooleanValue = value;
+        return this;
+    }
+
+    /**
+     * Returns the generic <code>boolean</code> value.
+     */
+    @VisibleForTesting
+    public boolean getBoolean() {
+        return mBooleanValue;
+    }
+
+    /**
+     * Sets a time period in milliseconds.
+     */
+    public DevicePolicyEventLogger setTimePeriod(long timePeriodMillis) {
+        mTimePeriodMs = timePeriodMillis;
+        return this;
+    }
+
+    /**
+     * Returns the time period in milliseconds.
+     */
+    @VisibleForTesting
+    public long getTimePeriod() {
+        return mTimePeriodMs;
+    }
+
+    /**
+     * Sets generic <code>String</code> values.
+     */
+    public DevicePolicyEventLogger setStrings(String... values) {
+        mStringArrayValue = values;
+        return this;
+    }
+
+    /**
+     * Sets generic <code>String</code> values.
+     * <p/>
+     * {@link #write()} logs the concatenation of <code>value</code> and <code>values</code>,
+     * in that order.
+     */
+    public DevicePolicyEventLogger setStrings(String value, String[] values) {
+        Preconditions.checkNotNull(values, "values parameter cannot be null");
+        mStringArrayValue = new String[values.length + 1];
+        mStringArrayValue[0] = value;
+        System.arraycopy(values, 0, mStringArrayValue, 1, values.length);
+        return this;
+    }
+
+    /**
+     * Sets generic <code>String</code> values.
+     * <p/>
+     * {@link #write()} logs the concatenation of <code>value1</code>, <code>value2</code>
+     * and <code>values</code>, in that order.
+     */
+    public DevicePolicyEventLogger setStrings(String value1, String value2, String[] values) {
+        Preconditions.checkNotNull(values, "values parameter cannot be null");
+        mStringArrayValue = new String[values.length + 2];
+        mStringArrayValue[0] = value1;
+        mStringArrayValue[1] = value2;
+        System.arraycopy(values, 0, mStringArrayValue, 2, values.length);
+        return this;
+    }
+
+    /**
+     * Returns the generic <code>String[]</code> value.
+     */
+    @VisibleForTesting
+    public String[] getStringArray() {
+        return mStringArrayValue;
+    }
+
+    /**
+     * Sets the package name of the admin application.
+     */
+    public DevicePolicyEventLogger setAdminPackageName(String packageName) {
+        mAdminPackageName = packageName;
+        return this;
+    }
+
+    /**
+     * Returns the package name of the admin application.
+     */
+    @VisibleForTesting
+    public String getAdminPackageName() {
+        return mAdminPackageName;
+    }
+
+    /**
+     * Writes the metric to {@link StatsLog}.
+     */
+    public void write() {
+        byte[] bytes = stringArrayValueToBytes(mStringArrayValue);
+        StatsLog.write(StatsLog.DEVICE_POLICY_EVENT, mEventId, mAdminPackageName, mIntValue,
+                mBooleanValue, mTimePeriodMs, bytes);
+    }
+
+    /**
+     * Converts the <code>String[] array</code> to <code>byte[]</code>.
+     * <p/>
+     * We can't log <code>String[]</code> using {@link StatsLog}. The convention is to assign
+     * the array to a proto object and convert it to <code>byte[]</code>.
+     */
+    private static byte[] stringArrayValueToBytes(String[] array) {
+        if (array == null) {
+            return null;
+        }
+        StringList stringList = new StringList();
+        stringList.stringValue = array;
+        return MessageNano.toByteArray(stringList);
+    }
+}