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

Commit 3755ba0f authored by Edgar Wang's avatar Edgar Wang
Browse files

Remove SettingsIntelligence log writer

Bug: 259034522
Test: rebuild
Change-Id: I9bc4eee86a251aa30289b2d279bdb84de82d91b1
parent 6bb2c735
Loading
Loading
Loading
Loading
+0 −211
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
    // Based on the exp, 99.5% users collect less than 150 data in 1 minute.
    private static final int CACHE_LOG_THRESHOLD = 150;

    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, int latency) {
        action(attribution /* from pageId */,
                SettingsEnums.PAGE_VISIBLE /* action */,
                pageId /* target pageId */,
                "" /* changedPreferenceKey */,
                latency /* changedPreferenceIntValue */);
    }

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

    @Override
    public void clicked(int sourceCategory, String key) {
    }

    @Override
    public void changed(int category, String key, int value) {
    }

    @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);
        });
        if (action == SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS
                || mSettingsLogList.size() >= CACHE_LOG_THRESHOLD) {
            // Directly send this event to notify SI instantly that the card is dismissed
            mLogHandler.sendLog();
        } else {
            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);
        // The data format is "size, length, byte array, length, byte array ..."
        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 {

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

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

        void sendLog() {
            removeCallbacks(mSendLogsRunnable);
            post(mSendLogsRunnable);
        }
    }

    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();
        }
    };
}
+0 −1
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ public class SettingsMetricsFeatureProvider extends MetricsFeatureProvider {
    protected void installLogWriters() {
        mLoggerWriters.add(new StatsLogWriter());
        mLoggerWriters.add(new SettingsEventLogWriter());
        mLoggerWriters.add(new SettingsIntelligenceLogWriter());
    }

    /**
+0 −78
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 static com.google.common.truth.Truth.assertThat;

import android.app.settings.SettingsEnums;
import android.content.Context;

import com.android.settings.intelligence.LogProto.SettingsLog;

import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class SettingsIntelligenceLogWriterTest {
    private Context mContext;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;
    }

    @Test
    public void serialize_hasSizeOne_returnCorrectData() throws IOException {
        final SettingsLog event = SettingsLog.newBuilder()
                .setAttribution(SettingsEnums.DASHBOARD_SUMMARY)
                .setAction(SettingsEnums.ACTION_SET_NEW_PASSWORD)
                .setPageId(SettingsEnums.SET_NEW_PASSWORD_ACTIVITY)
                .setChangedPreferenceKey("package")
                .setChangedPreferenceIntValue(100)
                .build();
        List<SettingsLog> events = new ArrayList<>();
        events.add(event);

        // execute
        final byte[] data = SettingsIntelligenceLogWriter.serialize(events);

        // parse data
        final ByteArrayInputStream bin = new ByteArrayInputStream(data);
        final DataInputStream inputStream = new DataInputStream(bin);
        final int size = inputStream.readInt();
        final byte[] change = new byte[inputStream.readInt()];
        inputStream.read(change);
        inputStream.close();
        final SettingsLog settingsLog = SettingsLog.parseFrom(change);

        // assert
        assertThat(events.size()).isEqualTo(size);
        assertThat(settingsLog.getAttribution()).isEqualTo(SettingsEnums.DASHBOARD_SUMMARY);
        assertThat(settingsLog.getAction()).isEqualTo(SettingsEnums.ACTION_SET_NEW_PASSWORD);
        assertThat(settingsLog.getPageId()).isEqualTo(SettingsEnums.SET_NEW_PASSWORD_ACTIVITY);
        assertThat(settingsLog.getChangedPreferenceKey()).isEqualTo("package");
        assertThat(settingsLog.getChangedPreferenceIntValue()).isEqualTo(100);
    }
}