Loading Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading protos/Android.bp +9 −0 Original line number Diff line number Diff line Loading @@ -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 protos/settings_log_bridge.proto 0 → 100644 +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; } src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriter.java 0 → 100644 +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(); } }; } src/com/android/settings/core/instrumentation/SettingsMetricsFeatureProvider.java +3 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -23,5 +25,6 @@ public class SettingsMetricsFeatureProvider extends MetricsFeatureProvider { protected void installLogWriters() { super.installLogWriters(); mLoggerWriters.add(new StatsLogWriter()); mLoggerWriters.add(new SettingsIntelligenceLogWriter()); } } Loading
Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
protos/Android.bp +9 −0 Original line number Diff line number Diff line Loading @@ -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
protos/settings_log_bridge.proto 0 → 100644 +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; }
src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriter.java 0 → 100644 +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(); } }; }
src/com/android/settings/core/instrumentation/SettingsMetricsFeatureProvider.java +3 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -23,5 +25,6 @@ public class SettingsMetricsFeatureProvider extends MetricsFeatureProvider { protected void installLogWriters() { super.installLogWriters(); mLoggerWriters.add(new StatsLogWriter()); mLoggerWriters.add(new SettingsIntelligenceLogWriter()); } }