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

Commit e22c2c16 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add SettingsIntelligenceLogwriter"

parents b3b4fcd8 02e45574
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
    guava \
    jsr305 \
    settings-contextual-card-protos-lite \
    settings-log-bridge-protos-lite \
    contextualcards \
    settings-logtags \
    zxing-core-1.7
+9 −0
Original line number Diff line number Diff line
@@ -6,3 +6,12 @@ java_library_static {
    },
    srcs: ["contextual_card_list.proto"],
}

java_library_static {
    name: "settings-log-bridge-protos-lite",
    host_supported: true,
    proto: {
        type: "lite",
    },
    srcs: ["settings_log_bridge.proto"],
}
 No newline at end of file
+37 −0
Original line number Diff line number Diff line
syntax = "proto2";

package com.android.settings.intelligence;
option java_outer_classname = "LogProto";

message SettingsLog {
  /**
   * Where this SettingsUIChange event comes from. For example, if
   * it's a PAGE_VISIBLE event, where the page is opened from.
   */
  optional int32 attribution = 1;

  /**
   * What the UI action is.
   */
  optional int32 action = 2;

  /**
   * Where the action is happening
   */
  optional int32 page_id = 3;

  /**
   * What preference changed in this event.
   */
  optional string changed_preference_key = 4;

  /**
   * The new value of the changed preference.
   */
  optional int32 changed_preference_int_value = 5;

  /**
   * The timestamp of a log event
   */
  optional string timestamp = 6;
}
+190 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 com.android.settings.core.instrumentation;

import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;

import androidx.annotation.VisibleForTesting;

import com.android.settings.R;
import com.android.settings.intelligence.LogProto.SettingsLog;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.LogWriter;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.LinkedList;
import java.util.List;

public class SettingsIntelligenceLogWriter implements LogWriter {
    private static final String TAG = "IntelligenceLogWriter";

    private static final String LOG = "logs";
    private static final long MESSAGE_DELAY = DateUtils.MINUTE_IN_MILLIS; // 1 minute

    private List<SettingsLog> mSettingsLogList;
    private SendLogHandler mLogHandler;

    public SettingsIntelligenceLogWriter() {
        mSettingsLogList = new LinkedList<>();
        final HandlerThread workerThread = new HandlerThread("SettingsIntelligenceLogWriter",
                Process.THREAD_PRIORITY_BACKGROUND);
        workerThread.start();
        mLogHandler = new SendLogHandler(workerThread.getLooper());
    }

    @Override
    public void visible(Context context, int attribution, int pageId) {
        action(attribution /* attribution */,
                SettingsEnums.PAGE_VISIBLE /* action */,
                pageId /* pageId */,
                "" /* changedPreferenceKey */,
                0 /* changedPreferenceIntValue */);
    }

    @Override
    public void hidden(Context context, int pageId) {
        action(SettingsEnums.PAGE_UNKNOWN /* attribution */,
                SettingsEnums.PAGE_HIDE /* action */,
                pageId /* pageId */,
                "" /* changedPreferenceKey */,
                0 /* changedPreferenceIntValue */);
    }

    @Override
    public void action(Context context, int action, Pair<Integer, Object>... taggedData) {
        action(SettingsEnums.PAGE_UNKNOWN /* attribution */,
                action,
                SettingsEnums.PAGE_UNKNOWN /* pageId */,
                "" /* changedPreferenceKey */,
                0 /* changedPreferenceIntValue */);
    }

    @Override
    public void action(Context context, int action, int value) {
        action(SettingsEnums.PAGE_UNKNOWN /* attribution */,
                action,
                SettingsEnums.PAGE_UNKNOWN /* pageId */,
                "" /* changedPreferenceKey */,
                value /* changedPreferenceIntValue */);
    }

    @Override
    public void action(Context context, int action, boolean value) {
        action(SettingsEnums.PAGE_UNKNOWN /* attribution */,
                action,
                SettingsEnums.PAGE_UNKNOWN /* pageId */,
                "" /* changedPreferenceKey */,
                value ? 1 : 0 /* changedPreferenceIntValue */);
    }

    @Override
    public void action(Context context, int action, String pkg) {
        action(SettingsEnums.PAGE_UNKNOWN /* attribution */,
                action,
                SettingsEnums.PAGE_UNKNOWN /* pageId */,
                pkg /* changedPreferenceKey */,
                1 /* changedPreferenceIntValue */);
    }

    @Override
    public void action(int attribution, int action, int pageId, String key, int value) {
        final ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault());
        final SettingsLog settingsLog = SettingsLog.newBuilder()
                .setAttribution(attribution)
                .setAction(action)
                .setPageId(pageId)
                .setChangedPreferenceKey(key != null ? key : "")
                .setChangedPreferenceIntValue(value)
                .setTimestamp(now.toString())
                .build();
        mLogHandler.post(() -> {
            mSettingsLogList.add(settingsLog);
        });
        mLogHandler.scheduleSendLog();
    }

    @VisibleForTesting
    static byte[] serialize(List<SettingsLog> settingsLogs) {
        final int size = settingsLogs.size();
        final ByteArrayOutputStream bout = new ByteArrayOutputStream();
        final DataOutputStream output = new DataOutputStream(bout);
        // Data is "size, length, bytearray, length, bytearray ..."
        try {
            output.writeInt(size);
            for (SettingsLog settingsLog : settingsLogs) {
                final byte[] data = settingsLog.toByteArray();
                output.writeInt(data.length);
                output.write(data);
            }
            return bout.toByteArray();
        } catch (Exception e) {
            Log.e(TAG, "serialize error", e);
            return null;
        } finally {
            try {
                output.close();
            } catch (Exception e) {
                Log.e(TAG, "close error", e);
            }
        }
    }

    private class SendLogHandler extends Handler {

        public SendLogHandler(Looper looper) {
            super(looper);
        }

        public void scheduleSendLog() {
            removeCallbacks(mSendLogsRunnable);
            postDelayed(mSendLogsRunnable, MESSAGE_DELAY);
        }
    }

    private final Runnable mSendLogsRunnable = () -> {
        final Context context = FeatureFactory.getAppContext();
        if (context == null) {
            Log.e(TAG, "context is null");
            return;
        }
        final String action = context.getString(R.string
                .config_settingsintelligence_log_action);
        if (!TextUtils.isEmpty(action) && !mSettingsLogList.isEmpty()) {
            final Intent intent = new Intent();
            intent.setPackage(context.getString(R.string
                    .config_settingsintelligence_package_name));
            intent.setAction(action);
            intent.putExtra(LOG, serialize(mSettingsLogList));
            context.sendBroadcastAsUser(intent, UserHandle.CURRENT);
            mSettingsLogList.clear();
        }
    };
}
+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.core.instrumentation;

import android.content.Context;

import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;

public class SettingsMetricsFeatureProvider extends MetricsFeatureProvider {
@@ -23,5 +25,6 @@ public class SettingsMetricsFeatureProvider extends MetricsFeatureProvider {
    protected void installLogWriters() {
        super.installLogWriters();
        mLoggerWriters.add(new StatsLogWriter());
        mLoggerWriters.add(new SettingsIntelligenceLogWriter());
    }
}
Loading