Merge "People Service data layer main structure"
diff --git a/apex/statsd/tests/libstatspull/Android.bp b/apex/statsd/tests/libstatspull/Android.bp
new file mode 100644
index 0000000..e813964
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/Android.bp
@@ -0,0 +1,56 @@
+// 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.
+
+android_test {
+ name: "LibStatsPullTests",
+ static_libs: [
+ "androidx.test.rules",
+ "platformprotoslite",
+ "statsdprotolite",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner.stubs",
+ "android.test.base.stubs",
+ ],
+ jni_libs: [
+ "libstatspull_testhelper",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "protos/**/*.proto",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ platform_apis: true,
+ privileged: true,
+ certificate: "platform",
+ compile_multilib: "both",
+}
+
+cc_library_shared {
+ name: "libstatspull_testhelper",
+ srcs: ["jni/stats_pull_helper.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libstatspull",
+ "libstatssocket",
+ ],
+}
\ No newline at end of file
diff --git a/apex/statsd/tests/libstatspull/AndroidManifest.xml b/apex/statsd/tests/libstatspull/AndroidManifest.xml
new file mode 100644
index 0000000..bffd400
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.os.statsd.libstats" >
+
+
+ <uses-permission android:name="android.permission.DUMP" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.os.statsd.libstats"
+ android:label="Tests for libstatspull">
+ </instrumentation>
+</manifest>
+
diff --git a/apex/statsd/tests/libstatspull/TEST_MAPPING b/apex/statsd/tests/libstatspull/TEST_MAPPING
new file mode 100644
index 0000000..5e1178cf
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit" : [
+ {
+ "name" : "LibStatsPullTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
new file mode 100644
index 0000000..e4ab823
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#include <binder/ProcessState.h>
+#include <jni.h>
+#include <log/log.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+
+#include <chrono>
+#include <thread>
+
+using std::this_thread::sleep_for;
+using namespace android;
+
+namespace {
+static int32_t sAtomTag;
+static int32_t sPullReturnVal;
+static int64_t sLatencyMillis;
+static int32_t sAtomsPerPull;
+static int32_t sNumPulls = 0;
+
+static bool initialized = false;
+
+static void init() {
+ if (!initialized) {
+ initialized = true;
+ // Set up the binder
+ sp<ProcessState> ps(ProcessState::self());
+ ps->setThreadPoolMaxThreadCount(9);
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ }
+}
+
+static status_pull_atom_return_t pullAtomCallback(int32_t atomTag, pulled_stats_event_list* data,
+ void* /*cookie*/) {
+ sNumPulls++;
+ sleep_for(std::chrono::milliseconds(sLatencyMillis));
+ for (int i = 0; i < sAtomsPerPull; i++) {
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event, atomTag);
+ stats_event_write_int64(event, (int64_t) sNumPulls);
+ stats_event_build(event);
+ }
+ return sPullReturnVal;
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_registerStatsPuller(
+ JNIEnv* /*env*/, jobject /* this */, jint atomTag, jlong timeoutNs, jlong coolDownNs,
+ jint pullRetVal, jlong latencyMillis, int atomsPerPull)
+{
+ init();
+ sAtomTag = atomTag;
+ sPullReturnVal = pullRetVal;
+ sLatencyMillis = latencyMillis;
+ sAtomsPerPull = atomsPerPull;
+ sNumPulls = 0;
+ pull_atom_metadata metadata = {.cool_down_ns = coolDownNs,
+ .timeout_ns = timeoutNs,
+ .additive_fields = nullptr,
+ .additive_fields_size = 0};
+ register_stats_pull_atom_callback(sAtomTag, &pullAtomCallback, &metadata, nullptr);
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_unregisterStatsPuller(
+ JNIEnv* /*env*/, jobject /* this */, jint /*atomTag*/)
+{
+ unregister_stats_pull_atom_callback(sAtomTag);
+}
+} // namespace
\ No newline at end of file
diff --git a/apex/statsd/tests/libstatspull/protos/test_atoms.proto b/apex/statsd/tests/libstatspull/protos/test_atoms.proto
new file mode 100644
index 0000000..56c1b53
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/protos/test_atoms.proto
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+syntax = "proto2";
+
+package com.android.internal.os.statsd.protos;
+
+option java_package = "com.android.internal.os.statsd.protos";
+option java_outer_classname = "TestAtoms";
+
+message PullCallbackAtomWrapper {
+ optional PullCallbackAtom pull_callback_atom = 150030;
+}
+
+message PullCallbackAtom {
+ optional int64 long_val = 1;
+}
+
+
+
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
new file mode 100644
index 0000000..d0d1400
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.internal.os.statsd;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.StatsManager;
+import android.util.Log;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto.AppBreadcrumbReported;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.GaugeBucketInfo;
+import com.android.os.StatsLog.GaugeMetricData;
+import com.android.os.StatsLog.StatsLogReport;
+import com.android.os.StatsLog.StatsdStatsReport;
+import com.android.os.StatsLog.StatsdStatsReport.ConfigStats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Util class for constructing statsd configs.
+ */
+public class StatsConfigUtils {
+ public static final String TAG = "statsd.StatsConfigUtils";
+ public static final int SHORT_WAIT = 2_000; // 2 seconds.
+
+ /**
+ * @return An empty StatsdConfig in serialized proto format.
+ */
+ public static StatsdConfig.Builder getSimpleTestConfig(long configId) {
+ return StatsdConfig.newBuilder().setId(configId)
+ .addAllowedLogSource(StatsConfigUtils.class.getPackage().getName());
+ }
+
+
+ public static boolean verifyValidConfigExists(StatsManager statsManager, long configId) {
+ StatsdStatsReport report = null;
+ try {
+ report = StatsdStatsReport.parser().parseFrom(statsManager.getStatsMetadata());
+ } catch (Exception e) {
+ Log.e(TAG, "getMetadata failed", e);
+ }
+ assertThat(report).isNotNull();
+ boolean foundConfig = false;
+ for (ConfigStats configStats : report.getConfigStatsList()) {
+ if (configStats.getId() == configId && configStats.getIsValid()
+ && configStats.getDeletionTimeSec() == 0) {
+ foundConfig = true;
+ }
+ }
+ return foundConfig;
+ }
+
+ public static AtomMatcher getAppBreadcrumbMatcher(long id, int label) {
+ return AtomMatcher.newBuilder()
+ .setId(id)
+ .setSimpleAtomMatcher(
+ SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
+ .setEqInt(label)
+ )
+ )
+ .build();
+ }
+
+ public static ConfigMetricsReport getConfigMetricsReport(StatsManager statsManager,
+ long configId) {
+ ConfigMetricsReportList reportList = null;
+ try {
+ reportList = ConfigMetricsReportList.parser()
+ .parseFrom(statsManager.getReports(configId));
+ } catch (Exception e) {
+ Log.e(TAG, "getData failed", e);
+ }
+ assertThat(reportList).isNotNull();
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
+ ConfigMetricsReport report = reportList.getReports(0);
+ assertThat(report.getDumpReportReason())
+ .isEqualTo(ConfigMetricsReport.DumpReportReason.GET_DATA_CALLED);
+ return report;
+
+ }
+ public static List<Atom> getGaugeMetricDataList(ConfigMetricsReport report) {
+ List<Atom> data = new ArrayList<>();
+ for (StatsLogReport metric : report.getMetricsList()) {
+ for (GaugeMetricData gaugeMetricData : metric.getGaugeMetrics().getDataList()) {
+ for (GaugeBucketInfo bucketInfo : gaugeMetricData.getBucketInfoList()) {
+ for (Atom atom : bucketInfo.getAtomList()) {
+ data.add(atom);
+ }
+ }
+ }
+ }
+ return data;
+ }
+
+ public static List<Atom> getGaugeMetricDataList(StatsManager statsManager, long configId) {
+ ConfigMetricsReport report = getConfigMetricsReport(statsManager, configId);
+ return getGaugeMetricDataList(report);
+ }
+}
+
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
new file mode 100644
index 0000000..dbd636d
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -0,0 +1,285 @@
+/*
+ * 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.internal.os.statsd.libstats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.Log;
+import android.util.StatsLog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
+import com.android.internal.os.statsd.StatsConfigUtils;
+import com.android.internal.os.statsd.protos.TestAtoms;
+import com.android.os.AtomsProto.Atom;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Test puller registration.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class LibStatsPullTests {
+ private static final String LOG_TAG = LibStatsPullTests.class.getSimpleName();
+ private static final int SHORT_SLEEP_MILLIS = 250;
+ private static final int LONG_SLEEP_MILLIS = 1_000;
+ private Context mContext;
+ private static final int PULL_ATOM_TAG = 150030;
+ private static final int APP_BREADCRUMB_LABEL = 3;
+ private static int sPullReturnValue;
+ private static long sConfigId;
+ private static long sPullLatencyMillis;
+ private static long sPullTimeoutNs;
+ private static long sCoolDownNs;
+ private static int sAtomsPerPull;
+
+ static {
+ System.loadLibrary("statspull_testhelper");
+ }
+
+ /**
+ * Setup the tests. Initialize shared data.
+ */
+ @Before
+ public void setup() {
+// Debug.waitForDebugger();
+ mContext = InstrumentationRegistry.getTargetContext();
+ assertThat(InstrumentationRegistry.getInstrumentation()).isNotNull();
+ sPullReturnValue = StatsManager.PULL_SUCCESS;
+ sPullLatencyMillis = 0;
+ sPullTimeoutNs = 10_000_000_000L;
+ sCoolDownNs = 1_000_000_000L;
+ sAtomsPerPull = 1;
+ }
+
+ /**
+ * Teardown the tests.
+ */
+ @After
+ public void tearDown() throws Exception {
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ statsManager.removeConfig(sConfigId);
+ }
+
+ /**
+ * Tests adding a puller callback and that pulls complete successfully.
+ */
+ @Test
+ public void testPullAtomCallbackRegistration() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ // Upload a config that captures that pulled atom.
+ createAndAddConfigToStatsd(statsManager);
+
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish.
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(1);
+ TestAtoms.PullCallbackAtomWrapper atomWrapper = null;
+ try {
+ atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser()
+ .parseFrom(data.get(0).toByteArray());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to parse primitive atoms");
+ }
+ assertThat(atomWrapper).isNotNull();
+ assertThat(atomWrapper.hasPullCallbackAtom()).isTrue();
+ TestAtoms.PullCallbackAtom atom =
+ atomWrapper.getPullCallbackAtom();
+ assertThat(atom.getLongVal()).isEqualTo(1);
+ }
+
+ /**
+ * Tests that a failed pull is skipped.
+ */
+ @Test
+ public void testPullAtomCallbackFailure() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ createAndAddConfigToStatsd(statsManager);
+ sPullReturnValue = StatsManager.PULL_SKIP;
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish.
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(0);
+ }
+
+ /**
+ * Tests that a pull that times out is skipped.
+ */
+ @Test
+ public void testPullAtomCallbackTimeout() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ createAndAddConfigToStatsd(statsManager);
+ // The puller will sleep for 1.5 sec.
+ sPullLatencyMillis = 1_500;
+ // 1 second timeout
+ sPullTimeoutNs = 1_000_000_000;
+
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish and the pull timeout.
+ Thread.sleep(sPullLatencyMillis * 2);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(0);
+ }
+
+ /**
+ * Tests that 2 pulls in quick succession use the cache instead of pulling again.
+ */
+ @Test
+ public void testPullAtomCallbackCache() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ createAndAddConfigToStatsd(statsManager);
+
+ // Set the cooldown to 10 seconds
+ sCoolDownNs = 10_000_000_000L;
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Pull from cache.
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(2);
+ for (int i = 0; i < data.size(); i++) {
+ TestAtoms.PullCallbackAtomWrapper atomWrapper = null;
+ try {
+ atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser()
+ .parseFrom(data.get(i).toByteArray());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to parse primitive atoms");
+ }
+ assertThat(atomWrapper).isNotNull();
+ assertThat(atomWrapper.hasPullCallbackAtom()).isTrue();
+ TestAtoms.PullCallbackAtom atom =
+ atomWrapper.getPullCallbackAtom();
+ assertThat(atom.getLongVal()).isEqualTo(1);
+ }
+ }
+
+ /**
+ * Tests that a pull that returns 1000 stats events works properly.
+ */
+ @Test
+ public void testPullAtomCallbackStress() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ // Upload a config that captures that pulled atom.
+ createAndAddConfigToStatsd(statsManager);
+ sAtomsPerPull = 1000;
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish.
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ statsManager.unregisterPullAtomCallback(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(sAtomsPerPull);
+
+ for (int i = 0; i < data.size(); i++) {
+ TestAtoms.PullCallbackAtomWrapper atomWrapper = null;
+ try {
+ atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser()
+ .parseFrom(data.get(i).toByteArray());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to parse primitive atoms");
+ }
+ assertThat(atomWrapper).isNotNull();
+ assertThat(atomWrapper.hasPullCallbackAtom()).isTrue();
+ TestAtoms.PullCallbackAtom atom =
+ atomWrapper.getPullCallbackAtom();
+ assertThat(atom.getLongVal()).isEqualTo(1);
+ }
+ }
+
+ private void createAndAddConfigToStatsd(StatsManager statsManager) throws Exception {
+ sConfigId = System.currentTimeMillis();
+ long triggerMatcherId = sConfigId + 10;
+ long pullerMatcherId = sConfigId + 11;
+ long metricId = sConfigId + 100;
+ StatsdConfig config = StatsConfigUtils.getSimpleTestConfig(sConfigId)
+ .addAtomMatcher(
+ StatsConfigUtils.getAppBreadcrumbMatcher(triggerMatcherId,
+ APP_BREADCRUMB_LABEL))
+ .addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(pullerMatcherId)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(PULL_ATOM_TAG))
+ )
+ .addGaugeMetric(GaugeMetric.newBuilder()
+ .setId(metricId)
+ .setWhat(pullerMatcherId)
+ .setTriggerEvent(triggerMatcherId)
+ .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true))
+ .setBucket(TimeUnit.CTS)
+ .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
+ .setMaxNumGaugeAtomsPerBucket(1000)
+ )
+ .build();
+ statsManager.addConfig(sConfigId, config.toByteArray());
+ assertThat(StatsConfigUtils.verifyValidConfigExists(statsManager, sConfigId)).isTrue();
+ }
+
+ private native void registerStatsPuller(int atomTag, long timeoutNs, long coolDownNs,
+ int pullReturnVal, long latencyMillis, int atomPerPull);
+
+ private native void unregisterStatsPuller(int atomTag);
+}
+
diff --git a/api/current.txt b/api/current.txt
index 2ff8136..06271c8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -43893,11 +43893,13 @@
method public android.graphics.drawable.Icon getIcon();
method public CharSequence getLabel();
method public int getState();
+ method @Nullable public CharSequence getStateDescription();
method @Nullable public CharSequence getSubtitle();
method public void setContentDescription(CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(CharSequence);
method public void setState(int);
+ method public void setStateDescription(@Nullable CharSequence);
method public void setSubtitle(@Nullable CharSequence);
method public void updateTile();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5ac00d0..1661c94 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -78,9 +78,9 @@
// Pushed atoms start at 2.
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
- BleScanStateChanged ble_scan_state_changed = 2;
+ BleScanStateChanged ble_scan_state_changed = 2 [(module) = "bluetooth"];
ProcessStateChanged process_state_changed = 3;
- BleScanResultReceived ble_scan_result_received = 4;
+ BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"];
SensorStateChanged sensor_state_changed = 5;
GpsScanStateChanged gps_scan_state_changed = 6;
SyncStateChanged sync_state_changed = 7;
@@ -144,7 +144,8 @@
AppDied app_died = 65;
ResourceConfigurationChanged resource_configuration_changed = 66;
BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
- BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
+ BluetoothConnectionStateChanged bluetooth_connection_state_changed =
+ 68 [(module) = "bluetooth"];
GpsSignalQualityChanged gps_signal_quality_changed = 69;
UsbConnectorStateChanged usb_connector_state_changed = 70;
SpeakerImpedanceReported speaker_impedance_reported = 71;
@@ -207,9 +208,12 @@
RescuePartyResetReported rescue_party_reset_reported = 122;
SignedConfigReported signed_config_reported = 123;
GnssNiEventReported gnss_ni_event_reported = 124;
- BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125;
- BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126;
- BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127;
+ BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event =
+ 125 [(module) = "bluetooth"];
+ BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed =
+ 126 [(module) = "bluetooth"];
+ BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed =
+ 127 [(module) = "bluetooth"];
AppDowngraded app_downgraded = 128;
AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129;
LowStorageStateChanged low_storage_state_changed = 130;
@@ -233,23 +237,40 @@
BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = 148;
BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150;
- BluetoothActiveDeviceChanged bluetooth_active_device_changed = 151;
- BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed = 152;
- BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed = 153;
- BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed = 154;
- BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported = 155;
- BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported = 156;
- BluetoothDeviceRssiReported bluetooth_device_rssi_reported = 157;
- BluetoothDeviceFailedContactCounterReported bluetooth_device_failed_contact_counter_reported = 158;
- BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported = 159;
- BluetoothHciTimeoutReported bluetooth_hci_timeout_reported = 160;
- BluetoothQualityReportReported bluetooth_quality_report_reported = 161;
- BluetoothDeviceInfoReported bluetooth_device_info_reported = 162;
- BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported = 163;
- BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported = 164;
- BluetoothBondStateChanged bluetooth_bond_state_changed = 165;
- BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166;
- BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
+ BluetoothActiveDeviceChanged bluetooth_active_device_changed =
+ 151 [(module) = "bluetooth"];
+ BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed =
+ 152 [(module) = "bluetooth"];
+ BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed =
+ 153 [(module) = "bluetooth"];
+ BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed =
+ 154 [(module) = "bluetooth"];
+ BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported =
+ 155 [(module) = "bluetooth"];
+ BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported =
+ 156 [(module) = "bluetooth"];
+ BluetoothDeviceRssiReported bluetooth_device_rssi_reported =
+ 157 [(module) = "bluetooth"];
+ BluetoothDeviceFailedContactCounterReported
+ bluetooth_device_failed_contact_counter_reported = 158 [(module) = "bluetooth"];
+ BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported =
+ 159 [(module) = "bluetooth"];
+ BluetoothHciTimeoutReported bluetooth_hci_timeout_reported =
+ 160 [(module) = "bluetooth"];
+ BluetoothQualityReportReported bluetooth_quality_report_reported =
+ 161 [(module) = "bluetooth"];
+ BluetoothDeviceInfoReported bluetooth_device_info_reported =
+ 162 [(module) = "bluetooth"];
+ BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported =
+ 163 [(module) = "bluetooth"];
+ BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported =
+ 164 [(module) = "bluetooth"];
+ BluetoothBondStateChanged bluetooth_bond_state_changed =
+ 165 [(module) = "bluetooth"];
+ BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported =
+ 166 [(module) = "bluetooth"];
+ BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported =
+ 167 [(module) = "bluetooth"];
ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168;
ProcessStartTime process_start_time = 169;
PermissionGrantRequestResultReported permission_grant_request_result_reported =
@@ -272,7 +293,8 @@
BiometricEnrolled biometric_enrolled = 184;
SystemServerWatchdogOccurred system_server_watchdog_occurred = 185;
TombStoneOccurred tomb_stone_occurred = 186;
- BluetoothClassOfDeviceReported bluetooth_class_of_device_reported = 187;
+ BluetoothClassOfDeviceReported bluetooth_class_of_device_reported =
+ 187 [(module) = "bluetooth"];
IntelligenceEventReported intelligence_event_reported =
188 [(module) = "intelligence"];
ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index cdb49f0..35d26ab 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3569,8 +3569,16 @@
* This field will be ignored by Launchers that don't support badging, don't show
* notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
*
+ * If this notification has {@link BubbleMetadata} attached that was created with
+ * {@link BubbleMetadata.Builder#createShortcutBubble(String)} a check will be performed
+ * to ensure the shortcutId supplied to bubble metadata matches the shortcutId set here,
+ * if one was set. If the shortcutId's were specified but do not match, an exception
+ * is thrown.
+ *
* @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
* supersedes
+ *
+ * @see BubbleMetadata.Builder#createShortcutBubble(String)
*/
@NonNull
public Builder setShortcutId(String shortcutId) {
@@ -5926,9 +5934,29 @@
/**
* Combine all of the options that have been set and return a new {@link Notification}
* object.
+ *
+ * If this notification has {@link BubbleMetadata} attached that was created with
+ * {@link BubbleMetadata.Builder#createShortcutBubble(String)} a check will be performed
+ * to ensure the shortcutId supplied to bubble metadata matches the shortcutId set on the
+ * notification builder, if one was set. If the shortcutId's were specified but do not
+ * match, an exception is thrown here.
+ *
+ * @see BubbleMetadata.Builder#createShortcutBubble(String)
+ * @see #setShortcutId(String)
*/
@NonNull
public Notification build() {
+ // Check shortcut id matches
+ if (mN.mShortcutId != null
+ && mN.mBubbleMetadata != null
+ && mN.mBubbleMetadata.getShortcutId() != null
+ && !mN.mShortcutId.equals(mN.mBubbleMetadata.getShortcutId())) {
+ throw new IllegalArgumentException(
+ "Notification and BubbleMetadata shortcut id's don't match,"
+ + " notification: " + mN.mShortcutId
+ + " vs bubble: " + mN.mBubbleMetadata.getShortcutId());
+ }
+
// first, add any extras from the calling code
if (mUserExtras != null) {
mN.extras = getAllExtras();
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 9333dbd..36f3a78 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -748,8 +748,8 @@
parcel.writeParcelableArray(mFieldClassificationIds, flags);
parcel.writeInt(mFlags);
parcel.writeIntArray(mCancelIds);
- parcel.writeInt(mRequestId);
parcel.writeParcelable(mInlineActions, flags);
+ parcel.writeInt(mRequestId);
}
public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR =
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 79c2152..40c0ac0 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -65,6 +65,7 @@
private CharSequence mLabel;
private CharSequence mSubtitle;
private CharSequence mContentDescription;
+ private CharSequence mStateDescription;
// Default to inactive until clients of the new API can update.
private int mState = STATE_INACTIVE;
@@ -177,6 +178,14 @@
}
/**
+ * Gets the current state description for the tile.
+ */
+ @Nullable
+ public CharSequence getStateDescription() {
+ return mStateDescription;
+ }
+
+ /**
* Sets the current content description for the tile.
*
* Does not take effect until {@link #updateTile()} is called.
@@ -187,6 +196,17 @@
this.mContentDescription = contentDescription;
}
+ /**
+ * Sets the current state description for the tile.
+ *
+ * Does not take effect until {@link #updateTile()} is called.
+ *
+ * @param stateDescription New state description to use.
+ */
+ public void setStateDescription(@Nullable CharSequence stateDescription) {
+ this.mStateDescription = stateDescription;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -215,6 +235,7 @@
TextUtils.writeToParcel(mLabel, dest, flags);
TextUtils.writeToParcel(mSubtitle, dest, flags);
TextUtils.writeToParcel(mContentDescription, dest, flags);
+ TextUtils.writeToParcel(mStateDescription, dest, flags);
}
private void readFromParcel(Parcel source) {
@@ -227,6 +248,7 @@
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mStateDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
public static final @android.annotation.NonNull Creator<Tile> CREATOR = new Creator<Tile>() {
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 4f1125f..007d367 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -314,7 +314,7 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
return env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Lnb"),
+ env->FindClass("android/media/tv/tuner/Lnb"),
gFields.lnbInitID,
mObject,
id);
@@ -373,7 +373,7 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject descramblerObj =
env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Descrambler"),
+ env->FindClass("android/media/tv/tuner/Descrambler"),
gFields.descramblerInitID,
mObject);
@@ -408,7 +408,7 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject filterObj =
env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Filter"),
+ env->FindClass("android/media/tv/tuner/filter/Filter"),
gFields.filterInitID,
mObject,
(jint) fId);
@@ -443,7 +443,7 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject dvrObj =
env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Dvr"),
+ env->FindClass("android/media/tv/tuner/dvr/Dvr"),
gFields.dvrInitID,
mObject);
sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
@@ -495,14 +495,14 @@
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
FrontendSettings frontendSettings;
- jclass clazz = env->FindClass("android/media/tv/tuner/FrontendSettings");
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "I");
uint32_t freq = static_cast<uint32_t>(env->GetIntField(clazz, freqField));
// TODO: handle the other 8 types of settings
if (type == 1) {
// analog
- clazz = env->FindClass("android/media/tv/tuner/FrontendSettings$FrontendAnalogSettings");
+ clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings");
FrontendAnalogType analogType =
static_cast<FrontendAnalogType>(
env->GetIntField(settings, env->GetFieldID(clazz, "mAnalogType", "I")));
@@ -525,7 +525,7 @@
static DvrSettings getDvrSettings(JNIEnv *env, jobject settings) {
DvrSettings dvrSettings;
- jclass clazz = env->FindClass("android/media/tv/tuner/DvrSettings");
+ jclass clazz = env->FindClass("android/media/tv/tuner/dvr/DvrSettings");
uint32_t statusMask =
static_cast<uint32_t>(env->GetIntField(
settings, env->GetFieldID(clazz, "mStatusMask", "I")));
@@ -585,23 +585,23 @@
gFields.frontendInitID =
env->GetMethodID(frontendClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
- jclass lnbClazz = env->FindClass("android/media/tv/tuner/Tuner$Lnb");
+ jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb");
gFields.lnbInitID =
env->GetMethodID(lnbClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
- jclass filterClazz = env->FindClass("android/media/tv/tuner/Tuner$Filter");
+ jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter");
gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
gFields.filterInitID =
env->GetMethodID(filterClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
gFields.onFilterStatusID =
env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
- jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Tuner$Descrambler");
+ jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Descrambler");
gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J");
gFields.descramblerInitID =
env->GetMethodID(descramblerClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
- jclass dvrClazz = env->FindClass("android/media/tv/tuner/Tuner$Dvr");
+ jclass dvrClazz = env->FindClass("android/media/tv/tuner/dvr/Dvr");
gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J");
gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
}
@@ -649,7 +649,7 @@
return 0;
}
-static jobjectArray android_media_tv_Tuner_get_frontend_status(JNIEnv, jobject, jintArray) {
+static jobject android_media_tv_Tuner_get_frontend_status(JNIEnv, jobject, jintArray) {
return NULL;
}
@@ -684,7 +684,7 @@
}
static jobject android_media_tv_Tuner_open_filter(
- JNIEnv *env, jobject thiz, jint type, jint subType, jint bufferSize) {
+ JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) {
sp<JTuner> tuner = getTuner(env, thiz);
DemuxFilterType filterType {
.mainType = static_cast<DemuxFilterMainType>(type),
@@ -708,17 +708,17 @@
env->GetObjectField(
filterSettingsObj,
env->GetFieldID(
- env->FindClass("android/media/tv/tuner/FilterSettings"),
+ env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"),
"mSettings",
- "Landroid/media/tv/tuner/FilterSettings$Settings;"));
+ "Landroid/media/tv/tuner/filter/Settings;"));
if (type == (int)DemuxFilterMainType::TS) {
// DemuxTsFilterSettings
- jclass clazz = env->FindClass("android/media/tv/tuner/FilterSettings$TsFilterSettings");
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I"));
if (subtype == (int)DemuxTsFilterType::PES) {
// DemuxFilterPesDataSettings
jclass settingClazz =
- env->FindClass("android/media/tv/tuner/FilterSettings$PesSettings");
+ env->FindClass("android/media/tv/tuner/filter/PesSettings");
int streamId = env->GetIntField(
settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I"));
bool isRaw = (bool)env->GetBooleanField(
@@ -831,7 +831,7 @@
}
static int android_media_tv_Tuner_read_filter_fmq(
- JNIEnv *env, jobject filter, jbyteArray buffer, jint offset, jint size) {
+ JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) {
sp<Filter> filterSp = getFilter(env, filter);
if (filterSp == NULL) {
ALOGD("Failed to read filter FMQ: filter not found");
@@ -901,9 +901,14 @@
return 0;
}
-static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint type, jint bufferSize) {
- sp<JTuner> tuner = getTuner(env, thiz);
- return tuner->openDvr(static_cast<DvrType>(type), bufferSize);
+static jobject android_media_tv_Tuner_open_dvr_recorder(
+ JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
+ return NULL;
+}
+
+static jobject android_media_tv_Tuner_open_dvr_playback(
+ JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
+ return NULL;
}
static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
@@ -1019,35 +1024,35 @@
ALOGD("set fd = %d", dvrSp->mFd);
}
-static int android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jint size) {
+static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong size) {
sp<Dvr> dvrSp = getDvr(env, dvr);
if (dvrSp == NULL) {
ALOGD("Failed to read dvr: dvr not found");
}
- int available = dvrSp->mDvrMQ->availableToWrite();
- int write = std::min(size, available);
+ long available = dvrSp->mDvrMQ->availableToWrite();
+ long write = std::min((long) size, available);
DvrMQ::MemTransaction tx;
- int ret = 0;
+ long ret = 0;
if (dvrSp->mDvrMQ->beginWrite(write, &tx)) {
auto first = tx.getFirstRegion();
auto data = first.getAddress();
- int length = first.getLength();
- int firstToWrite = std::min(length, write);
+ long length = first.getLength();
+ long firstToWrite = std::min(length, write);
ret = read(dvrSp->mFd, data, firstToWrite);
if (ret < firstToWrite) {
- ALOGW("[DVR] file to MQ, first region: %d bytes to write, but %d bytes written",
+ ALOGW("[DVR] file to MQ, first region: %ld bytes to write, but %ld bytes written",
firstToWrite, ret);
} else if (firstToWrite < write) {
- ALOGD("[DVR] write second region: %d bytes written, %d bytes in total", ret, write);
+ ALOGD("[DVR] write second region: %ld bytes written, %ld bytes in total", ret, write);
auto second = tx.getSecondRegion();
data = second.getAddress();
length = second.getLength();
int secondToWrite = std::min(length, write - firstToWrite);
ret += read(dvrSp->mFd, data, secondToWrite);
}
- ALOGD("[DVR] file to MQ: %d bytes need to be written, %d bytes written", write, ret);
+ ALOGD("[DVR] file to MQ: %ld bytes need to be written, %ld bytes written", write, ret);
if (!dvrSp->mDvrMQ->commitWrite(ret)) {
ALOGE("[DVR] Error: failed to commit write!");
}
@@ -1055,17 +1060,17 @@
} else {
ALOGE("dvrMq.beginWrite failed");
}
- return ret;
+ return (jlong) ret;
}
-static int android_media_tv_Tuner_read_dvr_from_array(
- JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
- jint /* size */) {
+static jlong android_media_tv_Tuner_read_dvr_from_array(
+ JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jlong /* offset */,
+ jlong /* size */) {
//TODO: impl
return 0;
}
-static int android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jint size) {
+static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong size) {
sp<Dvr> dvrSp = getDvr(env, dvr);
if (dvrSp == NULL) {
ALOGW("Failed to write dvr: dvr not found");
@@ -1079,28 +1084,28 @@
DvrMQ& dvrMq = dvrSp->getDvrMQ();
- int available = dvrMq.availableToRead();
- int toRead = std::min(size, available);
+ long available = dvrMq.availableToRead();
+ long toRead = std::min((long) size, available);
- int ret = 0;
+ long ret = 0;
DvrMQ::MemTransaction tx;
if (dvrMq.beginRead(toRead, &tx)) {
auto first = tx.getFirstRegion();
auto data = first.getAddress();
- int length = first.getLength();
- int firstToRead = std::min(length, toRead);
+ long length = first.getLength();
+ long firstToRead = std::min(length, toRead);
ret = write(dvrSp->mFd, data, firstToRead);
if (ret < firstToRead) {
- ALOGW("[DVR] MQ to file: %d bytes read, but %d bytes written", firstToRead, ret);
+ ALOGW("[DVR] MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret);
} else if (firstToRead < toRead) {
- ALOGD("[DVR] read second region: %d bytes read, %d bytes in total", ret, toRead);
+ ALOGD("[DVR] read second region: %ld bytes read, %ld bytes in total", ret, toRead);
auto second = tx.getSecondRegion();
data = second.getAddress();
length = second.getLength();
int secondToRead = toRead - firstToRead;
ret += write(dvrSp->mFd, data, secondToRead);
}
- ALOGD("[DVR] MQ to file: %d bytes to be read, %d bytes written", toRead, ret);
+ ALOGD("[DVR] MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret);
if (!dvrMq.commitRead(ret)) {
ALOGE("[DVR] Error: failed to commit read!");
}
@@ -1109,12 +1114,12 @@
ALOGE("dvrMq.beginRead failed");
}
- return ret;
+ return (jlong) ret;
}
-static int android_media_tv_Tuner_write_dvr_to_array(
- JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
- jint /* size */) {
+static jlong android_media_tv_Tuner_write_dvr_to_array(
+ JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jlong /* offset */,
+ jlong /* size */) {
//TODO: impl
return 0;
}
@@ -1126,49 +1131,51 @@
(void *)android_media_tv_Tuner_get_frontend_ids },
{ "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
(void *)android_media_tv_Tuner_open_frontend_by_id },
- { "nativeTune", "(ILandroid/media/tv/tuner/FrontendSettings;)I",
+ { "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
- { "nativeScan", "(ILandroid/media/tv/tuner/FrontendSettings;I)I",
+ { "nativeScan", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;I)I",
(void *)android_media_tv_Tuner_scan },
{ "nativeStopScan", "()I", (void *)android_media_tv_Tuner_stop_scan },
{ "nativeSetLnb", "(I)I", (void *)android_media_tv_Tuner_set_lnb },
{ "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna },
- { "nativeGetFrontendStatus", "([I)[Landroid/media/tv/tuner/FrontendStatus;",
+ { "nativeGetFrontendStatus", "([I)Landroid/media/tv/tuner/frontend/FrontendStatus;",
(void *)android_media_tv_Tuner_get_frontend_status },
- { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/Tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_gat_av_sync_hw_id },
{ "nativeGetAvSyncTime", "(I)J", (void *)android_media_tv_Tuner_gat_av_sync_time },
{ "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam },
{ "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam },
- { "nativeGetFrontendInfo", "(I)[Landroid/media/tv/tuner/FrontendInfo;",
+ { "nativeGetFrontendInfo", "(I)Landroid/media/tv/tuner/FrontendInfo;",
(void *)android_media_tv_Tuner_get_frontend_info },
- { "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
+ { "nativeOpenFilter", "(IIJ)Landroid/media/tv/tuner/Tuner/filter/Filter;",
(void *)android_media_tv_Tuner_open_filter },
- { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/Tuner$TimeFilter;",
+ { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/Tuner/filter/TimeFilter;",
(void *)android_media_tv_Tuner_open_time_filter },
{ "nativeGetLnbIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_lnb_ids },
- { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Tuner$Lnb;",
+ { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_id },
- { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Tuner$Descrambler;",
+ { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;",
(void *)android_media_tv_Tuner_open_descrambler },
- { "nativeOpenDvr", "(II)Landroid/media/tv/tuner/Tuner$Dvr;",
- (void *)android_media_tv_Tuner_open_dvr },
+ { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;",
+ (void *)android_media_tv_Tuner_open_dvr_recorder },
+ { "nativeOpenDvrPlayback", "(J)Landroid/media/tv/tuner/dvr/DvrPlayback;",
+ (void *)android_media_tv_Tuner_open_dvr_playback },
{ "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
(void *)android_media_tv_Tuner_get_demux_caps },
};
static const JNINativeMethod gFilterMethods[] = {
- { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/FilterSettings;)I",
+ { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I",
(void *)android_media_tv_Tuner_configure_filter },
{ "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
- { "nativeSetDataSource", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_set_filter_data_source },
{ "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
{ "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter },
{ "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
- { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_filter_fmq },
+ { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
};
@@ -1183,31 +1190,36 @@
};
static const JNINativeMethod gDescramblerMethods[] = {
- { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeAddPid", "(IILandroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_add_pid },
- { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeRemovePid", "(IILandroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_remove_pid },
{ "nativeSetKeyToken", "([B)I", (void *)android_media_tv_Tuner_set_key_token },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_descrambler },
};
static const JNINativeMethod gDvrMethods[] = {
- { "nativeAttachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_attach_filter },
- { "nativeDetachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_detach_filter },
- { "nativeConfigureDvr", "(Landroid/media/tv/tuner/DvrSettings;)I",
+ { "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
(void *)android_media_tv_Tuner_configure_dvr },
{ "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr },
{ "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
{ "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
- { "nativeSetFileDescriptor", "(Ljava/io/FileDescriptor;)V",
- (void *)android_media_tv_Tuner_dvr_set_fd },
- { "nativeRead", "(I)I", (void *)android_media_tv_Tuner_read_dvr },
- { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_dvr_from_array },
- { "nativeWrite", "(I)I", (void *)android_media_tv_Tuner_write_dvr },
- { "nativeWrite", "([BII)I", (void *)android_media_tv_Tuner_write_dvr_to_array },
+ { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
+};
+
+static const JNINativeMethod gDvrRecorderMethods[] = {
+ { "nativeWrite", "(J)J", (void *)android_media_tv_Tuner_write_dvr },
+ { "nativeWrite", "([BJJ)J", (void *)android_media_tv_Tuner_write_dvr_to_array },
+};
+
+static const JNINativeMethod gDvrPlaybackMethods[] = {
+ { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr },
+ { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array },
};
static const JNINativeMethod gLnbMethods[] = {
@@ -1225,35 +1237,49 @@
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Filter",
+ env, "android/media/tv/tuner/filter/Filter",
gFilterMethods,
NELEM(gFilterMethods)) != JNI_OK) {
ALOGE("Failed to register filter native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$TimeFilter",
+ env, "android/media/tv/tuner/filter/TimeFilter",
gTimeFilterMethods,
NELEM(gTimeFilterMethods)) != JNI_OK) {
ALOGE("Failed to register time filter native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Descrambler",
+ env, "android/media/tv/tuner/Descrambler",
gDescramblerMethods,
NELEM(gDescramblerMethods)) != JNI_OK) {
ALOGE("Failed to register descrambler native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Dvr",
+ env, "android/media/tv/tuner/dvr/Dvr",
gDvrMethods,
NELEM(gDvrMethods)) != JNI_OK) {
ALOGE("Failed to register dvr native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Lnb",
+ env, "android/media/tv/tuner/dvr/DvrRecorder",
+ gDvrRecorderMethods,
+ NELEM(gDvrRecorderMethods)) != JNI_OK) {
+ ALOGE("Failed to register dvr recorder native methods");
+ return false;
+ }
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/dvr/DvrPlayback",
+ gDvrPlaybackMethods,
+ NELEM(gDvrPlaybackMethods)) != JNI_OK) {
+ ALOGE("Failed to register dvr playback native methods");
+ return false;
+ }
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Lnb",
gLnbMethods,
NELEM(gLnbMethods)) != JNI_OK) {
ALOGE("Failed to register lnb native methods");
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 6518924..17f2f47 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -133,6 +133,7 @@
public CharSequence label;
public CharSequence secondaryLabel;
public CharSequence contentDescription;
+ public CharSequence stateDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
public boolean dualTarget = false;
@@ -151,6 +152,7 @@
|| !Objects.equals(other.label, label)
|| !Objects.equals(other.secondaryLabel, secondaryLabel)
|| !Objects.equals(other.contentDescription, contentDescription)
+ || !Objects.equals(other.stateDescription, stateDescription)
|| !Objects.equals(other.dualLabelContentDescription,
dualLabelContentDescription)
|| !Objects.equals(other.expandedAccessibilityClassName,
@@ -168,6 +170,7 @@
other.label = label;
other.secondaryLabel = secondaryLabel;
other.contentDescription = contentDescription;
+ other.stateDescription = stateDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.expandedAccessibilityClassName = expandedAccessibilityClassName;
other.disabledByPolicy = disabledByPolicy;
@@ -195,6 +198,7 @@
sb.append(",label=").append(label);
sb.append(",secondaryLabel=").append(secondaryLabel);
sb.append(",contentDescription=").append(contentDescription);
+ sb.append(",stateDescription=").append(stateDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
sb.append(",disabledByPolicy=").append(disabledByPolicy);
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1f13f8d..8d935ec 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2549,4 +2549,7 @@
<!-- Quick Controls strings [CHAR LIMIT=30] -->
<string name="quick_controls_title">Quick Controls</string>
+
+ <!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
+ <string name="tile_unavailable">Unvailable</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 411980b..557c64b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -191,6 +191,7 @@
mTile.setLabel(tile.getLabel());
mTile.setSubtitle(tile.getSubtitle());
mTile.setContentDescription(tile.getContentDescription());
+ mTile.setStateDescription(tile.getStateDescription());
mTile.setState(tile.getState());
}
@@ -345,6 +346,12 @@
state.contentDescription = state.label;
}
+ if (mTile.getStateDescription() != null) {
+ state.stateDescription = mTile.getStateDescription();
+ } else {
+ state.stateDescription = null;
+ }
+
if (state instanceof BooleanState) {
state.expandedAccessibilityClassName = Switch.class.getName();
((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 8b7f280..fda9e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -65,7 +65,6 @@
private String mAccessibilityClass;
private boolean mTileState;
private boolean mCollapsedView;
- private boolean mClicked;
private boolean mShowRippleEffect = true;
private final ImageView mBg;
@@ -234,13 +233,35 @@
setLongClickable(state.handlesLongClick);
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
+ final StringBuilder stateDescription = new StringBuilder();
+ switch (state.state) {
+ case Tile.STATE_UNAVAILABLE:
+ stateDescription.append(mContext.getString(R.string.tile_unavailable));
+ break;
+ case Tile.STATE_INACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_off));
+ }
+ break;
+ case Tile.STATE_ACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_on));
+ }
+ break;
+ default:
+ break;
+ }
+ if (!TextUtils.isEmpty(state.stateDescription)) {
+ stateDescription.append(", ");
+ stateDescription.append(state.stateDescription);
+ }
+ setStateDescription(stateDescription.toString());
mAccessibilityClass =
state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
boolean newState = ((BooleanState) state).value;
if (mTileState != newState) {
- mClicked = false;
mTileState = newState;
}
}
@@ -297,23 +318,10 @@
}
@Override
- public boolean performClick() {
- mClicked = true;
- return super.performClick();
- }
-
- @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
event.setClassName(mAccessibilityClass);
- if (Switch.class.getName().equals(mAccessibilityClass)) {
- boolean b = mClicked ? !mTileState : mTileState;
- String label = getResources()
- .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
- event.setContentDescription(label);
- event.setChecked(b);
- }
}
}
@@ -325,11 +333,6 @@
if (!TextUtils.isEmpty(mAccessibilityClass)) {
info.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
- boolean b = mClicked ? !mTileState : mTileState;
- String label = getResources()
- .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
- info.setText(label);
- info.setChecked(b);
info.setCheckable(true);
if (isLongClickable()) {
info.addAction(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9282a2e..361b6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -134,25 +134,27 @@
state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.secondaryLabel = TextUtils.emptyIfNull(
getSecondaryLabel(enabled, connecting, connected, state.isTransient));
+ state.contentDescription = state.label;
+ state.stateDescription = "";
if (enabled) {
if (connected) {
state.icon = new BluetoothConnectedTileIcon();
if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
state.label = mController.getConnectedDeviceName();
}
- state.contentDescription =
+ state.stateDescription =
mContext.getString(R.string.accessibility_bluetooth_name, state.label)
+ ", " + state.secondaryLabel;
} else if (state.isTransient) {
state.icon = ResourceIcon.get(
com.android.internal.R.drawable.ic_bluetooth_transient_animation);
- state.contentDescription = state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
} else {
state.icon =
ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth);
state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_bluetooth) + ","
- + mContext.getString(R.string.accessibility_not_connected);
+ R.string.accessibility_quick_settings_bluetooth);
+ state.stateDescription = mContext.getString(R.string.accessibility_not_connected);
}
state.state = Tile.STATE_ACTIVE;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 0e813d1..58de057 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -183,6 +183,7 @@
protected void handleUpdateState(BooleanState state, Object arg) {
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.contentDescription = state.label;
+ state.stateDescription = "";
state.value = false;
final List<CastDevice> devices = mController.getCastDevices();
boolean connecting = false;
@@ -192,8 +193,9 @@
if (device.state == CastDevice.STATE_CONNECTED) {
state.value = true;
state.secondaryLabel = getDeviceName(device);
- state.contentDescription = state.contentDescription + "," +
- mContext.getString(R.string.accessibility_cast_name, state.label);
+ state.stateDescription = state.stateDescription + ","
+ + mContext.getString(
+ R.string.accessibility_cast_name, state.label);
connecting = false;
break;
} else if (device.state == CastDevice.STATE_CONNECTING) {
@@ -217,9 +219,8 @@
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
state.secondaryLabel = noWifi;
- state.contentDescription = state.contentDescription + ", " + mContext.getString(
- R.string.accessibility_quick_settings_not_available, noWifi);
}
+ state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
mDetailAdapter.updateItems(devices);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 22470c7..d5f86c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -194,17 +194,13 @@
state.secondaryLabel = r.getString(R.string.cell_data_off);
}
-
- // TODO(b/77881974): Instead of switching out the description via a string check for
- // we need to have two strings provided by the MobileIconGroup.
- final CharSequence contentDescriptionSuffix;
+ state.contentDescription = state.label;
if (state.state == Tile.STATE_INACTIVE) {
- contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
+ // This information is appended later by converting the Tile.STATE_INACTIVE state.
+ state.stateDescription = "";
} else {
- contentDescriptionSuffix = state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
}
-
- state.contentDescription = state.label + ", " + contentDescriptionSuffix;
}
private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 52d1a5b3..9215da4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -240,6 +240,8 @@
zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
+ // Keeping the secondaryLabel in contentDescription instead of stateDescription is easier
+ // to understand.
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
state.contentDescription =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index dafdd89..792c364 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -102,14 +102,13 @@
}
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
state.secondaryLabel = "";
+ state.stateDescription = "";
if (!mFlashlightController.isAvailable()) {
state.icon = mIcon;
state.slash.isSlashed = true;
state.secondaryLabel = mContext.getString(
R.string.quick_settings_flashlight_camera_in_use);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_flashlight_unavailable)
- + ", " + state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
state.state = Tile.STATE_UNAVAILABLE;
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 001e094..fd6b936 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -147,6 +147,7 @@
state.secondaryLabel = getSecondaryLabel(
isTileActive, isTransient, isDataSaverEnabled, numConnectedDevices);
+ state.stateDescription = state.secondaryLabel;
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index fbdca3b..e617867 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -105,15 +105,8 @@
}
state.icon = mIcon;
state.slash.isSlashed = !state.value;
- if (locationEnabled) {
- state.label = mContext.getString(R.string.quick_settings_location_label);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_location_on);
- } else {
- state.label = mContext.getString(R.string.quick_settings_location_label);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_location_off);
- }
+ state.label = mContext.getString(R.string.quick_settings_location_label);
+ state.contentDescription = state.label;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.expandedAccessibilityClassName = Switch.class.getName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index b7ce101..6e8dcf3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -195,6 +195,7 @@
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
final StringBuffer minimalContentDescription = new StringBuffer();
+ final StringBuffer minimalStateDescription = new StringBuffer();
final Resources r = mContext.getResources();
if (isTransient) {
state.icon = ResourceIcon.get(
@@ -219,13 +220,14 @@
mContext.getString(R.string.quick_settings_wifi_label)).append(",");
if (state.value) {
if (wifiConnected) {
- minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
+ minimalStateDescription.append(cb.wifiSignalContentDescription);
minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
if (!TextUtils.isEmpty(state.secondaryLabel)) {
minimalContentDescription.append(",").append(state.secondaryLabel);
}
}
}
+ state.stateDescription = minimalStateDescription.toString();
state.contentDescription = minimalContentDescription.toString();
state.dualLabelContentDescription = r.getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7853dc3..e54ee51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -103,14 +103,11 @@
state.icon = mIcon;
if (state.value) {
state.slash.isSlashed = false;
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_work_mode_on);
} else {
state.slash.isSlashed = true;
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_work_mode_off);
}
state.label = mContext.getString(R.string.quick_settings_work_mode_label);
+ state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
index 7c413779..9fda125 100644
--- a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
@@ -28,4 +28,9 @@
public int getInterfaceVersion() {
return IDhcpServerCallbacks.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IDhcpServerCallbacks.HASH;
+ }
}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 190d2509..f39e7af 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -301,6 +301,11 @@
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
}
private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
diff --git a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
index 3218c0b..b1ffdb0 100644
--- a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
+++ b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
@@ -67,4 +67,9 @@
public int getInterfaceVersion() {
return INetdUnsolicitedEventListener.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return INetdUnsolicitedEventListener.HASH;
+ }
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 0b7029b..5602d1a8 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -92,6 +92,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -670,7 +671,7 @@
service.onDataSharedLocked(request,
new DataShareCallbackDelegate(request, clientCancellationSignal,
- clientAdapter));
+ clientAdapter, ContentCaptureManagerService.this));
}
}
@@ -918,20 +919,22 @@
}
}
- // TODO(b/148265162): DataShareCallbackDelegate should be a static class keeping week references
- // to the needed info
- private class DataShareCallbackDelegate extends IDataShareCallback.Stub {
+ private static class DataShareCallbackDelegate extends IDataShareCallback.Stub {
@NonNull private final DataShareRequest mDataShareRequest;
- @NonNull private final ICancellationSignal mClientCancellationSignal;
- @NonNull private final IDataShareWriteAdapter mClientAdapter;
+ @NonNull
+ private final WeakReference<ICancellationSignal> mClientCancellationSignalReference;
+ @NonNull private final WeakReference<IDataShareWriteAdapter> mClientAdapterReference;
+ @NonNull private final WeakReference<ContentCaptureManagerService> mParentServiceReference;
DataShareCallbackDelegate(@NonNull DataShareRequest dataShareRequest,
@NonNull ICancellationSignal clientCancellationSignal,
- @NonNull IDataShareWriteAdapter clientAdapter) {
+ @NonNull IDataShareWriteAdapter clientAdapter,
+ ContentCaptureManagerService parentService) {
mDataShareRequest = dataShareRequest;
- mClientCancellationSignal = clientCancellationSignal;
- mClientAdapter = clientAdapter;
+ mClientCancellationSignalReference = new WeakReference<>(clientCancellationSignal);
+ mClientAdapterReference = new WeakReference<>(clientAdapter);
+ mParentServiceReference = new WeakReference<>(parentService);
}
@Override
@@ -939,41 +942,52 @@
IDataShareReadAdapter serviceAdapter) throws RemoteException {
Slog.i(TAG, "Data share request accepted by Content Capture service");
+ final ContentCaptureManagerService parentService = mParentServiceReference.get();
+ final ICancellationSignal clientCancellationSignal =
+ mClientCancellationSignalReference.get();
+ final IDataShareWriteAdapter clientAdapter = mClientAdapterReference.get();
+ if (parentService == null || clientCancellationSignal == null
+ || clientAdapter == null) {
+ Slog.w(TAG, "Can't fulfill accept() request, because remote objects have been "
+ + "GC'ed");
+ return;
+ }
+
Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
if (clientPipe == null) {
- mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ clientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
return;
}
- ParcelFileDescriptor source_in = clientPipe.second;
- ParcelFileDescriptor sink_in = clientPipe.first;
+ ParcelFileDescriptor sourceIn = clientPipe.second;
+ ParcelFileDescriptor sinkIn = clientPipe.first;
Pair<ParcelFileDescriptor, ParcelFileDescriptor> servicePipe = createPipe();
if (servicePipe == null) {
- bestEffortCloseFileDescriptors(source_in, sink_in);
+ bestEffortCloseFileDescriptors(sourceIn, sinkIn);
- mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ clientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
return;
}
- ParcelFileDescriptor source_out = servicePipe.second;
- ParcelFileDescriptor sink_out = servicePipe.first;
+ ParcelFileDescriptor sourceOut = servicePipe.second;
+ ParcelFileDescriptor sinkOut = servicePipe.first;
ICancellationSignal cancellationSignalTransport =
CancellationSignal.createTransport();
- mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
+ parentService.mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
- mClientAdapter.write(source_in);
- serviceAdapter.start(sink_out, cancellationSignalTransport);
+ clientAdapter.write(sourceIn);
+ serviceAdapter.start(sinkOut, cancellationSignalTransport);
CancellationSignal cancellationSignal =
CancellationSignal.fromTransport(cancellationSignalTransport);
cancellationSignal.setOnCancelListener(() -> {
try {
- mClientCancellationSignal.cancel();
+ clientCancellationSignal.cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to propagate cancel operation to the caller", e);
}
@@ -982,13 +996,13 @@
// File descriptor received by the client app will be a copy of the current one. Close
// the one that belongs to the system server, so there's only 1 open left for the
// current pipe.
- bestEffortCloseFileDescriptor(source_in);
+ bestEffortCloseFileDescriptor(sourceIn);
- mDataShareExecutor.execute(() -> {
+ parentService.mDataShareExecutor.execute(() -> {
try (InputStream fis =
- new ParcelFileDescriptor.AutoCloseInputStream(sink_in);
+ new ParcelFileDescriptor.AutoCloseInputStream(sinkIn);
OutputStream fos =
- new ParcelFileDescriptor.AutoCloseOutputStream(source_out)) {
+ new ParcelFileDescriptor.AutoCloseOutputStream(sourceOut)) {
byte[] byteBuffer = new byte[DATA_SHARE_BYTE_BUFFER_LENGTH];
while (true) {
@@ -1005,52 +1019,86 @@
}
});
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- mPackagesWithShareRequests.remove(mDataShareRequest.getPackageName());
-
- // Interaction finished successfully <=> all data has been written to Content
- // Capture Service. If it hasn't been read successfully, service would be able
- // to signal through the cancellation signal.
- boolean finishedSuccessfully = !sink_in.getFileDescriptor().valid()
- && !source_out.getFileDescriptor().valid();
-
- if (finishedSuccessfully) {
- Slog.i(TAG, "Content capture data sharing session terminated "
- + "successfully for package '"
- + mDataShareRequest.getPackageName()
- + "'");
- } else {
- Slog.i(TAG, "Reached the timeout of Content Capture data sharing session "
- + "for package '"
- + mDataShareRequest.getPackageName()
- + "', terminating the pipe.");
- }
-
- // Ensure all the descriptors are closed after the session.
- bestEffortCloseFileDescriptors(source_in, sink_in, source_out, sink_out);
-
- if (!finishedSuccessfully) {
- try {
- mClientCancellationSignal.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to cancel() the client operation", e);
- }
- try {
- serviceCancellationSignal.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to cancel() the service operation", e);
- }
- }
- }
- }, MAX_DATA_SHARE_FILE_DESCRIPTORS_TTL_MS);
+ parentService.mHandler.postDelayed(() ->
+ enforceDataSharingTtl(
+ sourceIn,
+ sinkIn,
+ sourceOut,
+ sinkOut,
+ new WeakReference<>(serviceCancellationSignal)),
+ MAX_DATA_SHARE_FILE_DESCRIPTORS_TTL_MS);
}
@Override
public void reject() throws RemoteException {
Slog.i(TAG, "Data share request rejected by Content Capture service");
- mClientAdapter.rejected();
+ IDataShareWriteAdapter clientAdapter = mClientAdapterReference.get();
+ if (clientAdapter == null) {
+ Slog.w(TAG, "Can't fulfill reject() request, because remote objects have been "
+ + "GC'ed");
+ return;
+ }
+
+ clientAdapter.rejected();
+ }
+
+ private void enforceDataSharingTtl(ParcelFileDescriptor sourceIn,
+ ParcelFileDescriptor sinkIn,
+ ParcelFileDescriptor sourceOut,
+ ParcelFileDescriptor sinkOut,
+ WeakReference<ICancellationSignal> serviceCancellationSignalReference) {
+
+ final ContentCaptureManagerService parentService = mParentServiceReference.get();
+ final ICancellationSignal clientCancellationSignal =
+ mClientCancellationSignalReference.get();
+ final ICancellationSignal serviceCancellationSignal =
+ serviceCancellationSignalReference.get();
+ if (parentService == null || clientCancellationSignal == null
+ || serviceCancellationSignal == null) {
+ Slog.w(TAG, "Can't enforce data sharing TTL, because remote objects have been "
+ + "GC'ed");
+ return;
+ }
+
+ synchronized (parentService.mLock) {
+ parentService.mPackagesWithShareRequests
+ .remove(mDataShareRequest.getPackageName());
+
+ // Interaction finished successfully <=> all data has been written to Content
+ // Capture Service. If it hasn't been read successfully, service would be able
+ // to signal through the cancellation signal.
+ boolean finishedSuccessfully = !sinkIn.getFileDescriptor().valid()
+ && !sourceOut.getFileDescriptor().valid();
+
+ if (finishedSuccessfully) {
+ Slog.i(TAG, "Content capture data sharing session terminated "
+ + "successfully for package '"
+ + mDataShareRequest.getPackageName()
+ + "'");
+ } else {
+ Slog.i(TAG, "Reached the timeout of Content Capture data sharing session "
+ + "for package '"
+ + mDataShareRequest.getPackageName()
+ + "', terminating the pipe.");
+ }
+
+ // Ensure all the descriptors are closed after the session.
+ bestEffortCloseFileDescriptors(sourceIn, sinkIn, sourceOut, sinkOut);
+
+ if (!finishedSuccessfully) {
+ try {
+ clientCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel() the client operation", e);
+ }
+ try {
+ serviceCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel() the service operation", e);
+ }
+ }
+ }
}
private Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dd33566..d933e9d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2962,6 +2962,11 @@
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
}
private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 05d8360..9d4c783 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -735,6 +735,11 @@
public int getInterfaceVersion() {
return INetdUnsolicitedEventListener.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return INetdUnsolicitedEventListener.HASH;
+ }
}
//
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index f2892cc..17e2f69 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -310,6 +310,11 @@
return this.VERSION;
}
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
private void addWakeupEvent(WakeupEvent event) {
String iface = event.iface;
mWakeupEvents.append(event);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 26812f4..9e3292b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1595,11 +1595,11 @@
mRootWindowContainer.resumeFocusedStacksTopActivities(
mTargetStack, mStartActivity, mOptions);
}
- } else if (mStartActivity != null) {
- mSupervisor.mRecentTasks.add(mStartActivity.getTask());
}
mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
+ // Update the recent tasks list immediately when the activity starts
+ mSupervisor.mRecentTasks.add(mStartActivity.getTask());
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
preferredWindowingMode, mPreferredDisplayId, mTargetStack);
diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java
index 6f91e00..dcefb53 100644
--- a/services/net/java/android/net/IpMemoryStore.java
+++ b/services/net/java/android/net/IpMemoryStore.java
@@ -52,6 +52,11 @@
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
});
}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 7f723b1..a3618b4 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -189,6 +189,11 @@
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 0fc2bc5..bab877e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1008,4 +1008,18 @@
.setOutActivity(outActivity).execute();
assertThat(outActivity[0].inSplitScreenSecondaryWindowingMode()).isTrue();
}
+
+ @Test
+ public void testActivityStart_expectAddedToRecentTask() {
+ RecentTasks recentTasks = mock(RecentTasks.class);
+ mService.mStackSupervisor.setRecentTasks(recentTasks);
+ doReturn(true).when(recentTasks).isCallerRecents(anyInt());
+
+ final ActivityStarter starter = prepareStarter(0 /* flags */);
+
+ starter.setReason("testAddToTaskListOnActivityStart")
+ .execute();
+
+ verify(recentTasks, times(1)).add(any());
+ }
}