Merge "Removed the @hide annotation on various StateListDrawable APIs to better match the API surface exposed in ColorStateList."
diff --git a/Android.bp b/Android.bp
index e120efd..25eb2bd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1039,7 +1039,6 @@
],
api_filename: "public_api.txt",
removed_api_filename: "removed.txt",
- removed_dex_api_filename: "removed-dex.txt",
args: framework_docs_args + " -referenceonly -nodocs",
}
@@ -1108,6 +1107,25 @@
],
}
+droiddoc {
+ name: "hiddenapi-lists",
+ defaults: ["framework-docs-default"],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ":api-version-xml",
+ "core/java/overview.html",
+ ":current-support-api",
+ ],
+ private_dex_api_filename: "private-dex.txt",
+ removed_dex_api_filename: "removed-dex.txt",
+ args: framework_docs_args +
+ " -referenceonly" +
+ " -nodocs" +
+ " -showUnannotated" +
+ " -showAnnotation android.annotation.SystemApi" +
+ " -showAnnotation android.annotation.TestApi",
+}
+
filegroup {
name: "apache-http-stubs-sources",
srcs: [
diff --git a/Android.mk b/Android.mk
index 5a2e79d..360ce75 100644
--- a/Android.mk
+++ b/Android.mk
@@ -309,38 +309,6 @@
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
-# ==== the complete hidden api list ===================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := hidden-api-list
-
-LOCAL_DROIDDOC_OPTIONS:=\
- $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
- -referenceonly \
- -showUnannotated \
- -showAnnotation android.annotation.SystemApi \
- -showAnnotation android.annotation.TestApi \
- -privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
- -nodocs
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_DROIDDOC)
-
-$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-
# ==== check javadoc comments but don't generate docs ========
include $(CLEAR_VARS)
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 6156a0c..b5a76cf 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -10,6 +10,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
+ androidx.annotation_annotation \
apct-perftests-utils \
guava
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 132a2f9..13c24d9 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -16,6 +16,16 @@
</intent-filter>
</activity>
<service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
+
+ <service
+ android:name="android.view.autofill.MyAutofillService"
+ android:label="PERF AutofillService"
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService" />
+ </intent-filter>
+ </service>
+
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/apct-tests/perftests/core/res/layout/autofill_dataset_picker_text_only.xml b/apct-tests/perftests/core/res/layout/autofill_dataset_picker_text_only.xml
new file mode 100644
index 0000000..5e6b277
--- /dev/null
+++ b/apct-tests/perftests/core/res/layout/autofill_dataset_picker_text_only.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/apct-tests/perftests/core/res/layout/test_autofill_login.xml b/apct-tests/perftests/core/res/layout/test_autofill_login.xml
new file mode 100644
index 0000000..b35bdf1
--- /dev/null
+++ b/apct-tests/perftests/core/res/layout/test_autofill_login.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/username_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/username_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username" />
+
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:imeOptions="flagNoFullscreen" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"
+ android:imeOptions="flagNoFullscreen" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <Button
+ android:id="@+id/login"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Login" />
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Cancel" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/output"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/apct-tests/perftests/core/src/android/view/autofill/AutofillPerfTest.java b/apct-tests/perftests/core/src/android/view/autofill/AutofillPerfTest.java
new file mode 100644
index 0000000..8acd6eb
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/autofill/AutofillPerfTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.autofill;
+
+import android.app.Activity;
+import android.os.Looper;
+import android.os.Bundle;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.SettingsHelper;
+import android.perftests.utils.SettingsStateKeeperRule;
+import android.perftests.utils.ShellHelper;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.StubActivity;
+import android.provider.Settings;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.perftests.core.R;
+
+import java.util.Locale;
+import java.util.Collection;
+import java.util.Arrays;
+
+import org.junit.Test;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class AutofillPerfTest {
+ @Parameters(name = "{0}")
+ @SuppressWarnings("rawtypes")
+ public static Collection layouts() {
+ return Arrays.asList(new Object[][] {
+ { "Simple login", R.layout.test_autofill_login}
+ });
+ }
+
+ private final int mLayoutId;
+ private EditText mUsername;
+ private EditText mPassword;
+
+ public AutofillPerfTest(String key, int layoutId) {
+ mLayoutId = layoutId;
+ }
+
+ @ClassRule
+ public static final SettingsStateKeeperRule mServiceSettingsKeeper = new SettingsStateKeeperRule(
+ InstrumentationRegistry.getTargetContext(), Settings.Secure.AUTOFILL_SERVICE);
+
+ @Rule
+ public ActivityTestRule<StubActivity> mActivityRule =
+ new ActivityTestRule<StubActivity>(StubActivity.class);
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Prepares the activity so that by the time the test is run it has reference to its fields.
+ */
+ @Before
+ public void prepareActivity() throws Throwable {
+ mActivityRule.runOnUiThread(() -> {
+ assertTrue("We should be running on the main thread",
+ Looper.getMainLooper().getThread() == Thread.currentThread());
+ assertTrue("We should be running on the main thread",
+ Looper.myLooper() == Looper.getMainLooper());
+ Activity activity = mActivityRule.getActivity();
+ activity.setContentView(mLayoutId);
+ View root = activity.getWindow().getDecorView();
+ mUsername = root.findViewById(R.id.username);
+ mPassword = root.findViewById(R.id.password);
+ });
+ }
+
+ @Before
+ public void resetStaticState() {
+ MyAutofillService.resetStaticState();
+ }
+
+ /**
+ * This is the baseline test for focusing the 2 views when autofill is disabled.
+ */
+ @Test
+ public void testFocus_noService() throws Throwable {
+ resetService();
+
+ focusTest(false);
+ }
+
+ /**
+ * This time the service is called, but it returns a {@code null} response so the UI behaves
+ * as if autofill was disabled.
+ */
+ @Test
+ public void testFocus_serviceDoesNotAutofill() throws Throwable {
+ MyAutofillService.newCannedResponse().reply();
+ setService();
+
+ focusTest(true);
+
+ // Sanity check
+ MyAutofillService.assertNoAsyncErrors();
+ }
+
+ /**
+ * Now the service returns autofill data, for both username and password.
+ */
+ @Test
+ public void testFocus_autofillBothFields() throws Throwable {
+ MyAutofillService.newCannedResponse()
+ .setUsername(mUsername.getAutofillId(), "user")
+ .setPassword(mPassword.getAutofillId(), "pass")
+ .reply();
+ setService();
+
+ focusTest(true);
+
+ // Sanity check
+ MyAutofillService.assertNoAsyncErrors();
+ }
+
+ /**
+ * Now the service returns autofill data, but just for username.
+ */
+ @Test
+ public void testFocus_autofillUsernameOnly() throws Throwable {
+ // Must set ignored ids so focus on password does not trigger new requests
+ MyAutofillService.newCannedResponse()
+ .setUsername(mUsername.getAutofillId(), "user")
+ .setIgnored(mPassword.getAutofillId())
+ .reply();
+ setService();
+
+ focusTest(true);
+
+ // Sanity check
+ MyAutofillService.assertNoAsyncErrors();
+ }
+
+ private void focusTest(boolean waitForService) throws Throwable {
+ // Must first focus in a field to trigger autofill and wait for service response
+ // outside the loop
+ mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ if (waitForService) {
+ MyAutofillService.getLastFillRequest();
+ }
+ mActivityRule.runOnUiThread(() -> {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mUsername.requestFocus();
+ mPassword.requestFocus();
+ }
+ });
+ }
+
+ /**
+ * This is the baseline test for changing the 2 views when autofill is disabled.
+ */
+ @Test
+ public void testChange_noService() throws Throwable {
+ resetService();
+
+ changeTest(false);
+ }
+
+ /**
+ * This time the service is called, but it returns a {@code null} response so the UI behaves
+ * as if autofill was disabled.
+ */
+ @Test
+ public void testChange_serviceDoesNotAutofill() throws Throwable {
+ MyAutofillService.newCannedResponse().reply();
+ setService();
+
+ changeTest(true);
+
+ // Sanity check
+ MyAutofillService.assertNoAsyncErrors();
+ }
+
+ /**
+ * Now the service returns autofill data, for both username and password.
+ */
+ @Test
+ public void testChange_autofillBothFields() throws Throwable {
+ MyAutofillService.newCannedResponse()
+ .setUsername(mUsername.getAutofillId(), "user")
+ .setPassword(mPassword.getAutofillId(), "pass")
+ .reply();
+ setService();
+
+ changeTest(true);
+
+ // Sanity check
+ MyAutofillService.assertNoAsyncErrors();
+ }
+
+ /**
+ * Now the service returns autofill data, but just for username.
+ */
+ @Test
+ public void testChange_autofillUsernameOnly() throws Throwable {
+ // Must set ignored ids so focus on password does not trigger new requests
+ MyAutofillService.newCannedResponse()
+ .setUsername(mUsername.getAutofillId(), "user")
+ .setIgnored(mPassword.getAutofillId())
+ .reply();
+ setService();
+
+ changeTest(true);
+
+ // Sanity check
+ MyAutofillService.assertNoAsyncErrors();
+ }
+
+ private void changeTest(boolean waitForService) throws Throwable {
+ // Must first focus in a field to trigger autofill and wait for service response
+ // outside the loop
+ mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ if (waitForService) {
+ MyAutofillService.getLastFillRequest();
+ }
+ mActivityRule.runOnUiThread(() -> {
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mUsername.setText("");
+ mUsername.setText("a");
+ mPassword.setText("");
+ mPassword.setText("x");
+ }
+ });
+ }
+
+ /**
+ * Uses the {@code settings} binary to set the autofill service.
+ */
+ private void setService() {
+ SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE,
+ MyAutofillService.COMPONENT_NAME);
+ }
+
+ /**
+ * Uses the {@code settings} binary to reset the autofill service.
+ */
+ private void resetService() {
+ SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE);
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/view/autofill/MyAutofillService.java b/apct-tests/perftests/core/src/android/view/autofill/MyAutofillService.java
new file mode 100644
index 0000000..16eeb3b
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/autofill/MyAutofillService.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.view.autofill;
+
+import android.os.CancellationSignal;
+import android.service.autofill.AutofillService;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillCallback;
+import android.service.autofill.FillRequest;
+import android.service.autofill.FillResponse;
+import android.service.autofill.SaveCallback;
+import android.service.autofill.SaveRequest;
+import android.util.Log;
+import android.util.Pair;
+import android.widget.RemoteViews;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.perftests.core.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An {@link AutofillService} implementation whose replies can be programmed by the test case.
+ */
+public class MyAutofillService extends AutofillService {
+
+ private static final String TAG = "MyAutofillService";
+ private static final int TIMEOUT_MS = 5000;
+
+ private static final String PACKAGE_NAME = "com.android.perftests.core";
+ static final String COMPONENT_NAME = PACKAGE_NAME + "/android.view.autofill.MyAutofillService";
+
+ private static final BlockingQueue<FillRequest> sFillRequests = new LinkedBlockingQueue<>();
+ private static final BlockingQueue<CannedResponse> sCannedResponses =
+ new LinkedBlockingQueue<>();
+ private static final List<String> sAsyncErrors = new ArrayList<>();
+
+ /**
+ * Resets the static state associated with the service.
+ */
+ static void resetStaticState() {
+ sFillRequests.clear();
+ sCannedResponses.clear();
+ sAsyncErrors.clear();
+ }
+
+ /**
+ * Throws an exception if an error happened asynchronously while handing
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
+ */
+ static void assertNoAsyncErrors() {
+ if (!sAsyncErrors.isEmpty()) {
+ throw new IllegalStateException("got errors: " + sAsyncErrors);
+ }
+ }
+
+ /**
+ * Gets the the last {@link FillRequest} passed to
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} or throws an
+ * exception if that method was not called.
+ */
+ @NonNull
+ static FillRequest getLastFillRequest() {
+ FillRequest request = null;
+ try {
+ request = sFillRequests.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("onFillRequest() interrupted");
+ }
+ if (request == null) {
+ throw new IllegalStateException("onFillRequest() not called in " + TIMEOUT_MS + "ms");
+ }
+ return request;
+ }
+
+ @Override
+ public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
+ FillCallback callback) {
+ CannedResponse response = null;
+ try {
+ response = sCannedResponses.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ addAsyncError("onFillRequest() interrupted");
+ Thread.currentThread().interrupt();
+ return;
+ }
+ if (response == null) {
+ addAsyncError("onFillRequest() called without setting a response");
+ return;
+ }
+ try {
+ Dataset.Builder dataset = new Dataset.Builder(newDatasetPresentation("dataset"));
+ boolean hasData = false;
+ if (response.mUsername != null) {
+ hasData = true;
+ dataset.setValue(response.mUsername.first,
+ AutofillValue.forText(response.mUsername.second));
+ }
+ if (response.mPassword != null) {
+ hasData = true;
+ dataset.setValue(response.mPassword.first,
+ AutofillValue.forText(response.mPassword.second));
+ }
+ if (hasData) {
+ FillResponse.Builder fillResponse = new FillResponse.Builder();
+ if (response.mIgnoredIds != null) {
+ fillResponse.setIgnoredIds(response.mIgnoredIds);
+ }
+
+ callback.onSuccess(fillResponse.addDataset(dataset.build()).build());
+ } else {
+ callback.onSuccess(null);
+ }
+ } catch (Exception e) {
+ addAsyncError(e, callback);
+ }
+ sFillRequests.offer(request);
+ }
+
+ @Override
+ public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+ // No current test should have triggered it...
+ callback.onFailure("should not have called onSave");
+ }
+
+ static final class CannedResponse {
+ private final Pair<AutofillId, String> mUsername;
+ private final Pair<AutofillId, String> mPassword;
+ private final AutofillId[] mIgnoredIds;
+
+ private CannedResponse(@NonNull Builder builder) {
+ mUsername = builder.mUsername;
+ mPassword = builder.mPassword;
+ mIgnoredIds = builder.mIgnoredIds;
+ }
+
+ static class Builder {
+ private Pair<AutofillId, String> mUsername;
+ private Pair<AutofillId, String> mPassword;
+ private AutofillId[] mIgnoredIds;
+
+ @NonNull
+ Builder setUsername(@NonNull AutofillId id, @NonNull String value) {
+ mUsername = new Pair<>(id, value);
+ return this;
+ }
+
+ @NonNull
+ Builder setPassword(@NonNull AutofillId id, @NonNull String value) {
+ mPassword = new Pair<>(id, value);
+ return this;
+ }
+
+ @NonNull
+ Builder setIgnored(AutofillId... ids) {
+ mIgnoredIds = ids;
+ return this;
+ }
+
+ void reply() {
+ sCannedResponses.add(new CannedResponse(this));
+ }
+ }
+ }
+
+ /**
+ * Sets the expected canned {@link FillResponse} for the next
+ * {@link AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
+ */
+ static CannedResponse.Builder newCannedResponse() {
+ return new CannedResponse.Builder();
+ }
+
+ private void addAsyncError(@NonNull String error) {
+ sAsyncErrors.add(error);
+ Log.e(TAG, error);
+ }
+
+ private void addAsyncError(@NonNull Exception e, @NonNull FillCallback callback) {
+ String msg = e.toString();
+ sAsyncErrors.add(msg);
+ Log.e(TAG, "async error", e);
+ callback.onFailure(msg);
+ }
+
+ @NonNull
+ private static RemoteViews newDatasetPresentation(@NonNull CharSequence text) {
+ RemoteViews presentation =
+ new RemoteViews(PACKAGE_NAME, R.layout.autofill_dataset_picker_text_only);
+ presentation.setTextViewText(R.id.text, text);
+ return presentation;
+ }
+}
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index 9bc7d05..5ff4ebc 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -21,7 +21,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
- ub-uiautomator
+ apct-perftests-utils
LOCAL_PACKAGE_NAME := MultiUserPerfTests
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 629e6f4..7b65bfa 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -17,10 +17,8 @@
import android.os.Bundle;
import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.uiautomator.UiDevice;
+import android.perftests.utils.ShellHelper;
-import java.io.IOException;
import java.util.ArrayList;
// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
@@ -74,12 +72,7 @@
private void prepareForNextRun() {
SystemClock.sleep(COOL_OFF_PERIOD_MS);
- try {
- UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
- .executeShellCommand("am wait-for-broadcast-idle");
- } catch (IOException e) {
- throw new IllegalStateException("Cannot execute shell command", e);
- }
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle");
mStartTimeNs = System.nanoTime();
mPausedDurationNs = 0;
}
diff --git a/apct-tests/perftests/utils/Android.mk b/apct-tests/perftests/utils/Android.mk
index 55c13b0..604f0ad 100644
--- a/apct-tests/perftests/utils/Android.mk
+++ b/apct-tests/perftests/utils/Android.mk
@@ -1,7 +1,9 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ androidx.annotation_annotation
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/OneTimeSettingsListener.java b/apct-tests/perftests/utils/src/android/perftests/utils/OneTimeSettingsListener.java
new file mode 100644
index 0000000..37af4c7
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/OneTimeSettingsListener.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import static android.perftests.utils.SettingsHelper.NAMESPACE_SECURE;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper used to block tests until a secure settings value has been updated.
+ */
+public final class OneTimeSettingsListener extends ContentObserver {
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final ContentResolver mResolver;
+ private final String mKey;
+ private final int mTimeoutMs;
+
+ public OneTimeSettingsListener(@NonNull Context context, @NonNull String namespace,
+ @NonNull String key, int timeoutMs) {
+ super(new Handler(Looper.getMainLooper()));
+ mKey = key;
+ mResolver = context.getContentResolver();
+ mTimeoutMs = timeoutMs;
+ final Uri uri;
+ switch (namespace) {
+ case NAMESPACE_SECURE:
+ uri = Settings.Secure.getUriFor(key);
+ break;
+ default:
+ throw new IllegalArgumentException("invalid namespace: " + namespace);
+ }
+ mResolver.registerContentObserver(uri, false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ mResolver.unregisterContentObserver(this);
+ mLatch.countDown();
+ }
+
+ /**
+ * Blocks for a few seconds until it's called, or throws an {@link IllegalStateException} if
+ * it isn't.
+ */
+ public void assertCalled() {
+ try {
+ final boolean updated = mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS);
+ if (!updated) {
+ throw new IllegalStateException(
+ "Settings " + mKey + " not called in " + mTimeoutMs + "ms");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("Interrupted", e);
+ }
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/SettingsHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsHelper.java
new file mode 100644
index 0000000..d7d1d6b
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsHelper.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Provides utilities to interact with the device's {@link Settings}.
+ */
+public final class SettingsHelper {
+
+ public static final String NAMESPACE_SECURE = "secure";
+
+ private static int DEFAULT_TIMEOUT_MS = 5000;
+
+ /**
+ * Uses a Shell command to "asynchronously" set the given preference, returning right away.
+ */
+ public static void set(@NonNull String namespace, @NonNull String key, @Nullable String value) {
+ if (value == null) {
+ delete(namespace, key);
+ return;
+ }
+ ShellHelper.runShellCommand("settings put %s %s %s default", namespace, key, value);
+ }
+
+ /**
+ * Uses a Shell command to "synchronously" set the given preference by registering a listener
+ * and wait until it's set.
+ */
+ public static void syncSet(@NonNull Context context, @NonNull String namespace,
+ @NonNull String key, @Nullable String value) {
+ if (value == null) {
+ syncDelete(context, namespace, key);
+ return;
+ }
+
+ String currentValue = get(namespace, key);
+ if (value.equals(currentValue)) {
+ // Already set, ignore
+ return;
+ }
+
+ OneTimeSettingsListener observer = new OneTimeSettingsListener(context, namespace, key,
+ DEFAULT_TIMEOUT_MS);
+ set(namespace, key, value);
+ observer.assertCalled();
+ assertNewValue(namespace, key, value);
+ }
+
+ /**
+ * Uses a Shell command to "asynchronously" delete the given preference, returning right away.
+ */
+ public static void delete(@NonNull String namespace, @NonNull String key) {
+ ShellHelper.runShellCommand("settings delete %s %s", namespace, key);
+ }
+
+ /**
+ * Uses a Shell command to "synchronously" delete the given preference by registering a listener
+ * and wait until it's called.
+ */
+ public static void syncDelete(@NonNull Context context, @NonNull String namespace,
+ @NonNull String key) {
+ String currentValue = get(namespace, key);
+ if (currentValue == null || currentValue.equals("null")) {
+ // Already set, ignore
+ return;
+ }
+
+ OneTimeSettingsListener observer = new OneTimeSettingsListener(context, namespace, key,
+ DEFAULT_TIMEOUT_MS);
+ delete(namespace, key);
+ observer.assertCalled();
+ assertNewValue(namespace, key, "null");
+ }
+
+ /**
+ * Gets the value of a given preference using Shell command.
+ */
+ @NonNull
+ public static String get(@NonNull String namespace, @NonNull String key) {
+ return ShellHelper.runShellCommand("settings get %s %s", namespace, key);
+ }
+
+ private static void assertNewValue(@NonNull String namespace, @NonNull String key,
+ @Nullable String expectedValue) {
+ String actualValue = get(namespace, key);
+ if (!Objects.equals(actualValue, expectedValue)) {
+ throw new AssertionError("invalid value for " + namespace + ":" + key + ": expected '"
+ + actualValue + "' , got '" + expectedValue + "'");
+ }
+ }
+
+ private SettingsHelper() {
+ throw new UnsupportedOperationException("contain static methods only");
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/SettingsStateKeeperRule.java b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsStateKeeperRule.java
new file mode 100644
index 0000000..a8e2fdf
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsStateKeeperRule.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.perftests.utils;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+
+/**
+ * JUnit rule used to restore a {@link Settings} preference after the test is run.
+ *
+ * <p>It stores the current value before the test, and restores it after the test (if necessary).
+ */
+public class SettingsStateKeeperRule extends StateKeeperRule<String> {
+
+ /**
+ * Default constructor.
+ *
+ * @param context context used to retrieve the {@link Settings} provider.
+ * @param key prefence key.
+ */
+ public SettingsStateKeeperRule(@NonNull Context context, @NonNull String key) {
+ super(new SettingsStateManager(context, SettingsHelper.NAMESPACE_SECURE, key));
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/SettingsStateManager.java b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsStateManager.java
new file mode 100644
index 0000000..13ad66a
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/SettingsStateManager.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Manages the state of a preference backed by {@link Settings}.
+ */
+public class SettingsStateManager implements StateManager<String> {
+
+ private final Context mContext;
+ private final String mNamespace;
+ private final String mKey;
+
+ /**
+ * Default constructor.
+ *
+ * @param context context used to retrieve the {@link Settings} provider.
+ * @param namespace settings namespace.
+ * @param key prefence key.
+ */
+ public SettingsStateManager(@NonNull Context context, @NonNull String namespace,
+ @NonNull String key) {
+ mContext = context;
+ mNamespace = namespace;
+ mKey = key;
+ }
+
+ @Override
+ public void set(@Nullable String value) {
+ SettingsHelper.syncSet(mContext, mNamespace, mKey, value);
+ }
+
+ @Override
+ @Nullable
+ public String get() {
+ return SettingsHelper.get(mNamespace, mKey);
+ }
+
+ @Override
+ public String toString() {
+ return "SettingsStateManager[namespace=" + mNamespace + ", key=" + mKey + "]";
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
new file mode 100644
index 0000000..cae87fb
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.perftests.utils;
+
+import android.app.UiAutomation;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+
+import java.io.FileInputStream;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Provides Shell-based utilities such as running a command.
+ */
+public final class ShellHelper {
+
+ /**
+ * Runs a Shell command, returning a trimmed response.
+ */
+ @NonNull
+ public static String runShellCommand(@NonNull String template, Object...args) {
+ String command = String.format(template, args);
+ UiAutomation automan = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+ ParcelFileDescriptor pfd = automan.executeShellCommand(command);
+ byte[] buf = new byte[512];
+ int bytesRead;
+ try(FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ StringBuilder stdout = new StringBuilder();
+ while ((bytesRead = fis.read(buf)) != -1) {
+ stdout.append(new String(buf, 0, bytesRead));
+ }
+ String result = stdout.toString();
+ return TextUtils.isEmpty(result) ? "" : result.trim();
+ } catch (Exception e) {
+ throw new AndroidRuntimeException("Command '" + command + "' failed: ", e);
+ } finally {
+ // Must disconnect UI automation after every call, otherwise its accessibility service
+ // skews the performance tests.
+ automan.destroy();
+ }
+ }
+
+ private ShellHelper() {
+ throw new UnsupportedOperationException("contain static methods only");
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/StateKeeperRule.java b/apct-tests/perftests/utils/src/android/perftests/utils/StateKeeperRule.java
new file mode 100644
index 0000000..1bb09b2
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/StateKeeperRule.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import androidx.annotation.NonNull;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.Objects;
+
+/**
+ * JUnit rule used to restore a state after the test is run.
+ *
+ * <p>It stores the current state before the test, and restores it after the test (if necessary).
+ */
+public class StateKeeperRule<T> implements TestRule {
+
+ private final StateManager<T> mStateManager;
+
+ /**
+ * Default constructor.
+ *
+ * @param stateManager abstraction used to manage the state.
+ */
+ public StateKeeperRule(StateManager<T> stateManager) {
+ mStateManager = stateManager;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ final T previousValue = mStateManager.get();
+ try {
+ base.evaluate();
+ } finally {
+ final T currentValue = mStateManager.get();
+ if (!Objects.equals(previousValue, currentValue)) {
+ mStateManager.set(previousValue);
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/StateManager.java b/apct-tests/perftests/utils/src/android/perftests/utils/StateManager.java
new file mode 100644
index 0000000..5fc499a
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/StateManager.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.perftests.utils;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Abstraction for a state that is managed somewhere, like Android Settings.
+ */
+public interface StateManager<T> {
+
+ /**
+ * Sets a new state.
+ */
+ void set(@Nullable T value);
+
+ /**
+ * Gets the current state.
+ */
+ @Nullable T get();
+}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 2cbcca3..65ba4f7 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -114,7 +114,7 @@
// Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
// data subscriber that it's time to call getData.
- static const size_t kBytesPerConfigTriggerGetData = 128 * 1024;
+ static const size_t kBytesPerConfigTriggerGetData = 192 * 1024;
// Cap the UID map's memory usage to this. This should be fairly high since the UID information
// is critical for understanding the metrics.
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index e21392c..511aec3 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -122,6 +122,12 @@
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
}
+
+void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+ flushIfNeededLocked(dumpTimeNs);
+ mPastBuckets.clear();
+}
+
void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index cafc882..8e94a75 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -59,6 +59,8 @@
const bool include_current_partial_bucket,
android::util::ProtoOutputStream* protoOutput) override;
+ void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3661b31..5610492 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -438,6 +438,11 @@
mPastBuckets.clear();
}
+void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+ flushIfNeededLocked(dumpTimeNs);
+ mPastBuckets.clear();
+}
+
void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 80fbdde..f755294 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -65,6 +65,8 @@
const bool include_current_partial_bucket,
android::util::ProtoOutputStream* protoOutput) override;
+ void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 2f2679e..23f780a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -100,6 +100,10 @@
return buffer;
}
+void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+ mProto->clear();
+}
+
void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 5c29174..23302c4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -49,6 +49,7 @@
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
android::util::ProtoOutputStream* protoOutput) override;
+ void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1270856..005cb71 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -160,6 +160,12 @@
}
}
+void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+ flushIfNeededLocked(dumpTimeNs);
+ mPastBuckets.clear();
+ mSkippedBuckets.clear();
+}
+
void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 71d5912..8af2f36 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -91,6 +91,7 @@
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
android::util::ProtoOutputStream* protoOutput) override;
+ void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// for testing
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 532ecbf..4239649 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -119,6 +119,11 @@
return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, protoOutput);
}
+ void clearPastBuckets(const int64_t dumpTimeNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return clearPastBucketsLocked(dumpTimeNs);
+ }
+
void dumpStates(FILE* out, bool verbose) const {
std::lock_guard<std::mutex> lock(mMutex);
dumpStatesLocked(out, verbose);
@@ -177,6 +182,7 @@
virtual void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
android::util::ProtoOutputStream* protoOutput) = 0;
+ virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
virtual size_t byteSizeLocked() const = 0;
virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 47a1a86..0d3aebf 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -201,6 +201,8 @@
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, protoOutput);
protoOutput->end(token);
+ } else {
+ producer->clearPastBuckets(dumpTimeStampNs);
}
}
for (const auto& annotation : mAnnotations) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 27fd78f..506cf38 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -151,6 +151,12 @@
mPastBuckets.clear();
}
+void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+ flushIfNeededLocked(dumpTimeNs);
+ mPastBuckets.clear();
+ mSkippedBuckets.clear();
+}
+
void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 8df30d3..64604c2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -89,6 +89,7 @@
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
android::util::ProtoOutputStream* protoOutput) override;
+ void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 7d1ce94..4d0222e 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -803,6 +803,7 @@
Landroid/content/pm/PackageParser$Package;->mPreferredOrder:I
Landroid/content/pm/PackageParser$Package;->mSharedUserId:Ljava/lang/String;
Landroid/content/pm/PackageParser$Package;->mSharedUserLabel:I
+Landroid/content/pm/PackageParser$Package;->mSigningDetails:Landroid/content/pm/PackageParser$SigningDetails;
Landroid/content/pm/PackageParser$Package;->mVersionCode:I
Landroid/content/pm/PackageParser$Package;->mVersionName:Ljava/lang/String;
Landroid/content/pm/PackageParser$Package;->packageName:Ljava/lang/String;
@@ -819,6 +820,7 @@
Landroid/content/pm/PackageParser$ProviderIntentInfo;->provider:Landroid/content/pm/PackageParser$Provider;
Landroid/content/pm/PackageParser$Service;->info:Landroid/content/pm/ServiceInfo;
Landroid/content/pm/PackageParser$ServiceIntentInfo;->service:Landroid/content/pm/PackageParser$Service;
+Landroid/content/pm/PackageParser$SigningDetails;->signatures:[Landroid/content/pm/Signature;
Landroid/content/pm/PackageParser;-><init>()V
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
@@ -882,6 +884,7 @@
Landroid/content/res/DrawableCache;-><init>()V
Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
Landroid/content/res/ObbInfo;->salt:[B
+Landroid/content/res/Resources$Theme;->mThemeImpl:Landroid/content/res/ResourcesImpl$ThemeImpl;
Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
Landroid/content/res/Resources;->loadXmlResourceParser(ILjava/lang/String;)Landroid/content/res/XmlResourceParser;
Landroid/content/res/Resources;->loadXmlResourceParser(Ljava/lang/String;IILjava/lang/String;)Landroid/content/res/XmlResourceParser;
@@ -1457,6 +1460,7 @@
Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
Landroid/media/soundtrigger/SoundTriggerDetector$EventPayload;->getCaptureSession()Ljava/lang/Integer;
Landroid/media/soundtrigger/SoundTriggerDetector$EventPayload;->getData()[B
+Landroid/media/soundtrigger/SoundTriggerManager;->isRecognitionActive(Ljava/util/UUID;)Z
Landroid/media/soundtrigger/SoundTriggerManager;->loadSoundModel(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;)I
Landroid/media/soundtrigger/SoundTriggerManager;->startRecognition(Ljava/util/UUID;Landroid/os/Bundle;Landroid/content/ComponentName;Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I
Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
@@ -2273,6 +2277,7 @@
Landroid/renderscript/RenderScriptCacheDir;->mCacheDir:Ljava/io/File;
Landroid/renderscript/RenderScriptCacheDir;->setupDiskCache(Ljava/io/File;)V
Landroid/security/keystore/AndroidKeyStoreProvider;->getKeyStoreOperationHandle(Ljava/lang/Object;)J
+Landroid/security/keystore/recovery/RecoveryController;->getRecoveryStatus(Ljava/lang/String;Ljava/lang/String;)I
Landroid/security/keystore/recovery/RecoveryController;->initRecoveryService(Ljava/lang/String;[B)V
Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
Landroid/security/net/config/RootTrustManager;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
@@ -2371,6 +2376,7 @@
Landroid/telephony/SignalStrength;->getCdmaLevel()I
Landroid/telephony/SignalStrength;->getDbm()I
Landroid/telephony/SignalStrength;->getGsmDbm()I
+Landroid/telephony/SignalStrength;->getLteCqi()I
Landroid/telephony/SignalStrength;->getLteDbm()I
Landroid/telephony/SignalStrength;->getLteRsrp()I
Landroid/telephony/SignalStrength;->getLteRsrq()I
@@ -2961,6 +2967,7 @@
Landroid/webkit/WebView;->getVisibleTitleHeight()I
Landroid/webkit/WebView;->isPaused()Z
Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->mWebViewThread:Landroid/os/Looper;
Landroid/webkit/WebView;->notifyFindDialogDismissed()V
Landroid/webkit/WebView;->onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V
Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z
@@ -3172,6 +3179,7 @@
Landroid/widget/ScrollView;->mOverflingDistance:I
Landroid/widget/ScrollView;->mOverscrollDistance:I
Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/SearchView$SearchAutoComplete;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
Landroid/widget/SearchView;->mCloseButton:Landroid/widget/ImageView;
Landroid/widget/SearchView;->mSearchButton:Landroid/widget/ImageView;
Landroid/widget/SearchView;->mSearchPlate:Landroid/view/View;
@@ -3225,6 +3233,8 @@
Landroid/widget/VideoView;->mUri:Landroid/net/Uri;
Landroid/widget/VideoView;->mVideoHeight:I
Landroid/widget/VideoView;->mVideoWidth:I
+Landroid/widget/ViewAnimator;->mFirstTime:Z
+Landroid/widget/ViewAnimator;->mWhichChild:I
Lcom/android/ims/internal/uce/common/CapInfo;-><init>()V
Lcom/android/ims/internal/uce/common/CapInfo;->setCapTimestamp(J)V
Lcom/android/ims/internal/uce/common/CapInfo;->setCdViaPresenceSupported(Z)V
@@ -3928,6 +3938,7 @@
Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
Ljava/util/HashMap$HashIterator;->hasNext()Z
+Ljava/util/HashMap$HashIterator;->remove()V
Ljava/util/HashMap;->modCount:I
Ljava/util/HashMap;->table:[Ljava/util/HashMap$Node;
Ljava/util/HashSet;->map:Ljava/util/HashMap;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 97c9fa5..7338bfe 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -236,6 +236,17 @@
int userId, Intent[] intents, Bundle bOptions);
/**
+ * Start activity {@code intent} without calling user-id check.
+ *
+ * - DO NOT call it with the calling UID cleared.
+ * - The caller must do the calling user ID check.
+ *
+ * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+ */
+ public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
+ Intent intent, @Nullable Bundle options, int userId);
+
+ /**
* Get the procstate for the UID. The return value will be between
* {@link ActivityManager#MIN_PROCESS_STATE} and {@link ActivityManager#MAX_PROCESS_STATE}.
* Note if the UID doesn't exist, it'll return {@link ActivityManager#PROCESS_STATE_NONEXISTENT}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9a491bc..9511786 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1048,6 +1048,22 @@
}
@Override
+ public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions) {
+ warnIfCallingFromSystemProcess();
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.prepareToLeaveProcess(this);
+ ActivityManager.getService().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
+ null, false, false, user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f18b92a..badb2f6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2724,7 +2724,40 @@
* @hide
*/
public static boolean areRemoteViewsChanged(Builder first, Builder second) {
- return !first.usesStandardHeader() || !second.usesStandardHeader();
+ if (!Objects.equals(first.usesStandardHeader(), second.usesStandardHeader())) {
+ return true;
+ }
+
+ if (areRemoteViewsChanged(first.mN.contentView, second.mN.contentView)) {
+ return true;
+ }
+ if (areRemoteViewsChanged(first.mN.bigContentView, second.mN.bigContentView)) {
+ return true;
+ }
+ if (areRemoteViewsChanged(first.mN.headsUpContentView, second.mN.headsUpContentView)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean areRemoteViewsChanged(RemoteViews first, RemoteViews second) {
+ if (first == null && second == null) {
+ return false;
+ }
+ if (first == null && second != null || first != null && second == null) {
+ return true;
+ }
+
+ if (!Objects.equals(first.getLayoutId(), second.getLayoutId())) {
+ return true;
+ }
+
+ if (!Objects.equals(first.getSequenceNumber(), second.getSequenceNumber())) {
+ return true;
+ }
+
+ return false;
}
/**
@@ -5104,6 +5137,10 @@
savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
+ String appName = savedBundle.getString(EXTRA_SUBSTITUTE_APP_NAME);
+ if (appName != null) {
+ publicExtras.putString(EXTRA_SUBSTITUTE_APP_NAME, appName);
+ }
mN.extras = publicExtras;
RemoteViews view;
if (ambient) {
diff --git a/core/java/android/app/slice/SliceMetrics.java b/core/java/android/app/slice/SliceMetrics.java
index 20c1390..746beaf 100644
--- a/core/java/android/app/slice/SliceMetrics.java
+++ b/core/java/android/app/slice/SliceMetrics.java
@@ -18,9 +18,11 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.metrics.LogMaker;
import android.net.Uri;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
/**
* Metrics interface for slices.
@@ -34,24 +36,38 @@
private static final String TAG = "SliceMetrics";
private MetricsLogger mMetricsLogger;
+ private LogMaker mLogMaker;
/**
* An object to be used throughout the life of a slice to register events.
*/
public SliceMetrics(@NonNull Context context, @NonNull Uri uri) {
mMetricsLogger = new MetricsLogger();
+ mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
+ mLogMaker.addTaggedData(MetricsEvent.FIELD_SLICE_AUTHORITY, uri.getAuthority());
+ mLogMaker.addTaggedData(MetricsEvent.FIELD_SLICE_PATH, uri.getPath());
}
/**
* To be called whenever the slice becomes visible to the user.
*/
public void logVisible() {
+ synchronized (mLogMaker) {
+ mLogMaker.setCategory(MetricsEvent.SLICE)
+ .setType(MetricsEvent.TYPE_OPEN);
+ mMetricsLogger.write(mLogMaker);
+ }
}
/**
* To be called whenever the slice becomes invisible to the user.
*/
public void logHidden() {
+ synchronized (mLogMaker) {
+ mLogMaker.setCategory(MetricsEvent.SLICE)
+ .setType(MetricsEvent.TYPE_CLOSE);
+ mMetricsLogger.write(mLogMaker);
+ }
}
/**
@@ -68,5 +84,12 @@
* @param subSlice The URI of the sub-slice that is the subject of the interaction.
*/
public void logTouch(int actionType, @NonNull Uri subSlice) {
+ synchronized (mLogMaker) {
+ mLogMaker.setCategory(MetricsEvent.SLICE)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.FIELD_SUBSLICE_AUTHORITY, subSlice.getAuthority())
+ .addTaggedData(MetricsEvent.FIELD_SUBSLICE_PATH, subSlice.getPath());
+ mMetricsLogger.write(mLogMaker);
+ }
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1b6b5a0..1b9e27c 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -394,6 +394,58 @@
public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
/**
+ * Device only has a display.
+ *
+ * @hide
+ */
+ public static final int IO_CAPABILITY_OUT = 0;
+
+ /**
+ * Device has a display and the ability to input Yes/No.
+ *
+ * @hide
+ */
+ public static final int IO_CAPABILITY_IO = 1;
+
+ /**
+ * Device only has a keyboard for entry but no display.
+ *
+ * @hide
+ */
+ public static final int IO_CAPABILITY_IN = 2;
+
+ /**
+ * Device has no Input or Output capability.
+ *
+ * @hide
+ */
+ public static final int IO_CAPABILITY_NONE = 3;
+
+ /**
+ * Device has a display and a full keyboard.
+ *
+ * @hide
+ */
+ public static final int IO_CAPABILITY_KBDISP = 4;
+
+ /**
+ * Maximum range value for Input/Output capabilities.
+ *
+ * <p>This should be updated when adding a new Input/Output capability. Other code
+ * like validation depends on this being accurate.
+ *
+ * @hide
+ */
+ public static final int IO_CAPABILITY_MAX = 5;
+
+ /**
+ * The Input/Output capability of the device is unknown.
+ *
+ * @hide
+ */
+ public static final int IO_CAPABILITY_UNKNOWN = 255;
+
+ /**
* Broadcast Action: The local Bluetooth adapter has started the remote
* device discovery process.
* <p>This usually involves an inquiry scan of about 12 seconds, followed
@@ -1225,6 +1277,106 @@
}
/**
+ * Returns the Input/Output capability of the device for classic Bluetooth.
+ *
+ * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+ * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
+ * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public int getIoCapability() {
+ if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getIoCapability();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage(), e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+ }
+
+ /**
+ * Sets the Input/Output capability of the device for classic Bluetooth.
+ *
+ * <p>Changing the Input/Output capability of a device only takes effect on restarting the
+ * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
+ * and {@link BluetoothAdapter#enable()} to see the changes.
+ *
+ * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+ * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
+ * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setIoCapability(int capability) {
+ if (getState() != STATE_ON) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.setIoCapability(capability);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage(), e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
+ * Returns the Input/Output capability of the device for BLE operations.
+ *
+ * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+ * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
+ * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public int getLeIoCapability() {
+ if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getLeIoCapability();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage(), e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+ }
+
+ /**
+ * Sets the Input/Output capability of the device for BLE operations.
+ *
+ * <p>Changing the Input/Output capability of a device only takes effect on restarting the
+ * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
+ * and {@link BluetoothAdapter#enable()} to see the changes.
+ *
+ * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+ * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
+ * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setLeIoCapability(int capability) {
+ if (getState() != STATE_ON) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.setLeIoCapability(capability);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage(), e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
* Get the current Bluetooth scan mode of the local Bluetooth adapter.
* <p>The Bluetooth scan mode determines if the local adapter is
* connectable and/or discoverable from remote Bluetooth devices.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ac21395..7a6b72e 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -848,7 +848,11 @@
return null;
}
try {
- return service.getRemoteName(this);
+ String name = service.getRemoteName(this);
+ if (name != null) {
+ return name.replaceAll("[\\t\\n\\r]+", " ");
+ }
+ return null;
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 64998a3..133f339 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1977,6 +1977,33 @@
/**
* Broadcast the given intent to all interested BroadcastReceivers, allowing
+ * an array of required permissions to be enforced. This call is asynchronous; it returns
+ * immediately, and you will continue executing while the receivers are run. No results are
+ * propagated from receivers and receivers can not abort the broadcast. If you want to allow
+ * receivers to propagate results or abort the broadcast, you must send an ordered broadcast
+ * using {@link #sendOrderedBroadcast(Intent, String)}.
+ *
+ * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param user The user to send the broadcast to.
+ * @param receiverPermissions Array of names of permissions that a receiver must hold
+ * in order to receive your broadcast.
+ * If null or empty, no permissions are required.
+ *
+ * @see android.content.BroadcastReceiver
+ * @see #registerReceiver
+ * @see #sendBroadcast(Intent)
+ * @see #sendOrderedBroadcast(Intent, String)
+ * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+ * @hide
+ */
+ public abstract void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions);
+
+ /**
+ * Broadcast the given intent to all interested BroadcastReceivers, allowing
* an optional required permission to be enforced. This
* call is asynchronous; it returns immediately, and you will continue
* executing while the receivers are run. No results are propagated from
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 1867a6d..bae99b8 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -456,6 +456,13 @@
}
/** @hide */
+ @Override
+ public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions) {
+ mBase.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions);
+ }
+
+ /** @hide */
@SystemApi
@Override
public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 206ed71..dec2cd4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3659,6 +3659,10 @@
* <p class="note">This is a protected intent that can only be sent by the system.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable} and the helper
+ * functions {@code ServiceStateTable.getUriForSubscriptionIdAndField} and
+ * {@code ServiceStateTable.getUriForSubscriptionId} to subscribe to changes to the ServiceState
+ * for a given subscription id and field with a ContentObserver or using JobScheduler.
*/
@Deprecated
@SystemApi
@@ -3674,6 +3678,7 @@
* @see android.telephony.ServiceState#STATE_POWER_OFF
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_REG_STATE}.
*/
@Deprecated
@SystemApi
@@ -3687,6 +3692,7 @@
* @see android.telephony.ServiceState#STATE_POWER_OFF
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_REG_STATE}.
*/
@Deprecated
@SystemApi
@@ -3697,6 +3703,7 @@
* type.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_ROAMING_TYPE}.
*/
@Deprecated
@SystemApi
@@ -3707,6 +3714,7 @@
* type.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_ROAMING_TYPE}.
*/
@Deprecated
@SystemApi
@@ -3718,6 +3726,8 @@
* {@code null} if the operator name is not known or unregistered.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_ALPHA_LONG}.
*/
@Deprecated
@SystemApi
@@ -3729,6 +3739,8 @@
* {@code null} if the operator name is not known or unregistered.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_ALPHA_SHORT}.
*/
@Deprecated
@SystemApi
@@ -3740,6 +3752,7 @@
* network.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_NUMERIC}.
*/
@Deprecated
@SystemApi
@@ -3751,6 +3764,8 @@
* {@code null} if the operator name is not known or unregistered.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_ALPHA_LONG}.
*/
@Deprecated
@SystemApi
@@ -3762,6 +3777,8 @@
* {@code null} if the operator name is not known or unregistered.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_ALPHA_SHORT}.
*/
@Deprecated
@SystemApi
@@ -3773,6 +3790,7 @@
* data operator.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_NUMERIC}.
*/
@Deprecated
@SystemApi
@@ -3784,6 +3802,8 @@
* Will be {@code true} if manual mode, {@code false} if automatic mode.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#IS_MANUAL_NETWORK_SELECTION}.
*/
@Deprecated
@SystemApi
@@ -3794,6 +3814,8 @@
* radio technology.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#RIL_VOICE_RADIO_TECHNOLOGY}.
*/
@Deprecated
@SystemApi
@@ -3804,6 +3826,8 @@
* radio technology.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#RIL_DATA_RADIO_TECHNOLOGY}.
*/
@Deprecated
@SystemApi
@@ -3815,6 +3839,7 @@
* Will be {@code true} if support, {@code false} otherwise.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#CSS_INDICATOR}.
*/
@Deprecated
@SystemApi
@@ -3825,6 +3850,7 @@
* id. {@code Integer.MAX_VALUE} if unknown.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#NETWORK_ID}.
*/
@Deprecated
@SystemApi
@@ -3835,6 +3861,7 @@
* {@code Integer.MAX_VALUE} if unknown.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#SYSTEM_ID}.
*/
@Deprecated
@SystemApi
@@ -3845,6 +3872,7 @@
* indicator if registered on a CDMA or EVDO system or {@code -1} if not.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#CDMA_ROAMING_INDICATOR}.
*/
@Deprecated
@SystemApi
@@ -3855,6 +3883,8 @@
* indicator from the PRL if registered on a CDMA or EVDO system {@code -1} if not.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#CDMA_DEFAULT_ROAMING_INDICATOR}.
*/
@Deprecated
@SystemApi
@@ -3866,6 +3896,7 @@
* {@code true} if in emergency only mode, {@code false} otherwise.
* @hide
* @removed
+ * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#IS_EMERGENCY_ONLY}.
*/
@Deprecated
@SystemApi
@@ -3877,6 +3908,8 @@
* {@code true} if registration indicates roaming, {@code false} otherwise
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#IS_DATA_ROAMING_FROM_REGISTRATION}.
*/
@Deprecated
@SystemApi
@@ -3889,6 +3922,8 @@
* {@code true} if carrier aggregation is in use, {@code false} otherwise.
* @hide
* @removed
+ * @deprecated Use
+ * {@link android.provider.Telephony.ServiceStateTable#IS_USING_CARRIER_AGGREGATION}.
*/
@Deprecated
@SystemApi
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index ae1c207..ba7710b 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
@@ -42,10 +43,10 @@
String callingPackage, String packageName, in UserHandle user);
ActivityInfo resolveActivity(
String callingPackage, in ComponentName component, in UserHandle user);
- void startActivityAsUser(String callingPackage,
+ void startActivityAsUser(in IApplicationThread caller, String callingPackage,
in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
- void showAppDetailsAsUser(
+ void showAppDetailsAsUser(in IApplicationThread caller,
String callingPackage, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
boolean isPackageEnabled(String callingPackage, String packageName, in UserHandle user);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8717601..fa423e2 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -548,7 +548,8 @@
Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
}
try {
- mService.startActivityAsUser(mContext.getPackageName(),
+ mService.startActivityAsUser(mContext.getIApplicationThread(),
+ mContext.getPackageName(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -568,7 +569,8 @@
Rect sourceBounds, Bundle opts) {
logErrorForInvalidProfileAccess(user);
try {
- mService.showAppDetailsAsUser(mContext.getPackageName(),
+ mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
+ mContext.getPackageName(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index 6e51060..baa7a50 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -269,6 +269,29 @@
mBundle = in.readBundle();
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("RadioMetadata[");
+
+ final String removePrefix = "android.hardware.radio.metadata";
+
+ boolean first = true;
+ for (String key : mBundle.keySet()) {
+ if (first) first = false;
+ else sb.append(", ");
+
+ String keyDisp = key;
+ if (key.startsWith(removePrefix)) keyDisp = key.substring(removePrefix.length());
+
+ sb.append(keyDisp);
+ sb.append('=');
+ sb.append(mBundle.get(key));
+ }
+
+ sb.append("]");
+ return sb.toString();
+ }
+
/**
* Returns {@code true} if the given key is contained in the meta data
*
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 848c596..f17e0f0 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -77,10 +77,18 @@
private Printer mLogging;
private long mTraceTag;
- /* If set, the looper will show a warning log if a message dispatch takes longer than time. */
+ /**
+ * If set, the looper will show a warning log if a message dispatch takes longer than this.
+ */
private long mSlowDispatchThresholdMs;
- /** Initialize the current thread as a looper.
+ /**
+ * If set, the looper will show a warning log if a message delivery (actual delivery time -
+ * post time) takes longer than this.
+ */
+ private long mSlowDeliveryThresholdMs;
+
+ /** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
@@ -138,6 +146,16 @@
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
+ // Allow overriding a threshold with a system prop. e.g.
+ // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
+ final int thresholdOverride =
+ SystemProperties.getInt("log.looper."
+ + Process.myUid() + "."
+ + Thread.currentThread().getName()
+ + ".slow", 0);
+
+ boolean slowDeliveryDetected = false;
+
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
@@ -152,30 +170,50 @@
msg.callback + ": " + msg.what);
}
- final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
-
final long traceTag = me.mTraceTag;
+ long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
+ long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
+ if (thresholdOverride > 0) {
+ slowDispatchThresholdMs = thresholdOverride;
+ slowDeliveryThresholdMs = thresholdOverride;
+ }
+ final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
+ final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
+
+ final boolean needStartTime = logSlowDelivery || logSlowDispatch;
+ final boolean needEndTime = logSlowDispatch;
+
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
- final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
- final long end;
+
+ final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
+ final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
- end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
+ dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
- if (slowDispatchThresholdMs > 0) {
- final long time = end - start;
- if (time > slowDispatchThresholdMs) {
- Slog.w(TAG, "Dispatch took " + time + "ms on "
- + Thread.currentThread().getName() + ", h=" +
- msg.target + " cb=" + msg.callback + " msg=" + msg.what);
+ if (logSlowDelivery) {
+ if (slowDeliveryDetected) {
+ if ((dispatchStart - msg.when) <= 10) {
+ Slog.w(TAG, "Drained");
+ slowDeliveryDetected = false;
+ }
+ } else {
+ if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
+ msg)) {
+ // Once we write a slow delivery log, suppress until the queue drains.
+ slowDeliveryDetected = true;
+ }
}
}
+ if (logSlowDispatch) {
+ showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
+ }
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
@@ -196,6 +234,19 @@
}
}
+ private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
+ String what, Message msg) {
+ final long actualTime = measureEnd - measureStart;
+ if (actualTime < threshold) {
+ return false;
+ }
+ // For slow delivery, the current message isn't really important, but log it anyway.
+ Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
+ + Thread.currentThread().getName() + " h="
+ + msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
+ return true;
+ }
+
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
@@ -243,9 +294,13 @@
mTraceTag = traceTag;
}
- /** {@hide} */
- public void setSlowDispatchThresholdMs(long slowDispatchThresholdMs) {
+ /**
+ * Set a thresholds for slow dispatch/delivery log.
+ * {@hide}
+ */
+ public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
mSlowDispatchThresholdMs = slowDispatchThresholdMs;
+ mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6c57d88..f1dcaf9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1785,6 +1785,12 @@
public static final int USER_SETUP_PERSONALIZATION_STARTED = 1;
/**
+ * User has snoozed personalization and will complete it later.
+ * @hide
+ */
+ public static final int USER_SETUP_PERSONALIZATION_PAUSED = 2;
+
+ /**
* User has completed setup personalization.
* @hide
*/
@@ -1795,6 +1801,7 @@
@IntDef({
USER_SETUP_PERSONALIZATION_NOT_STARTED,
USER_SETUP_PERSONALIZATION_STARTED,
+ USER_SETUP_PERSONALIZATION_PAUSED,
USER_SETUP_PERSONALIZATION_COMPLETE
})
public @interface UserSetupPersonalization {}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 309fa4a..510626b 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,6 +16,10 @@
package android.service.notification;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.NotificationManager;
@@ -68,6 +72,7 @@
public static final int SOURCE_STAR = 2;
public static final int MAX_SOURCE = SOURCE_STAR;
private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
+ private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
@@ -93,13 +98,10 @@
private static final boolean DEFAULT_ALLOW_REMINDERS = false;
private static final boolean DEFAULT_ALLOW_EVENTS = false;
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
- private static final boolean DEFAULT_ALLOW_SCREEN_OFF = false;
- private static final boolean DEFAULT_ALLOW_SCREEN_ON = false;
private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
- private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
- Policy.getAllSuppressedVisualEffects();
+ private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
- public static final int XML_VERSION = 7;
+ public static final int XML_VERSION = 8;
public static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
private static final String ZEN_ATT_USER = "user";
@@ -151,12 +153,10 @@
public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
- public int allowCallsFrom = DEFAULT_SOURCE;
+ public int allowCallsFrom = DEFAULT_CALLS_SOURCE;
public int allowMessagesFrom = DEFAULT_SOURCE;
public int user = UserHandle.USER_SYSTEM;
public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
- public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
- public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
public int version;
@@ -185,8 +185,6 @@
automaticRules.put(ids[i], rules[i]);
}
}
- allowWhenScreenOff = source.readInt() == 1;
- allowWhenScreenOn = source.readInt() == 1;
allowAlarms = source.readInt() == 1;
allowMedia = source.readInt() == 1;
allowSystem = source.readInt() == 1;
@@ -219,8 +217,6 @@
} else {
dest.writeInt(0);
}
- dest.writeInt(allowWhenScreenOff ? 1 : 0);
- dest.writeInt(allowWhenScreenOn ? 1 : 0);
dest.writeInt(allowAlarms ? 1 : 0);
dest.writeInt(allowMedia ? 1 : 0);
dest.writeInt(allowSystem ? 1 : 0);
@@ -242,8 +238,6 @@
.append(",allowMessages=").append(allowMessages)
.append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
.append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
- .append(",allowWhenScreenOff=").append(allowWhenScreenOff)
- .append(",allowWhenScreenOn=").append(allowWhenScreenOn)
.append(",suppressedVisualEffects=").append(suppressedVisualEffects)
.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
.append(",automaticRules=").append(automaticRules)
@@ -289,12 +283,6 @@
if (allowMessagesFrom != to.allowMessagesFrom) {
d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
}
- if (allowWhenScreenOff != to.allowWhenScreenOff) {
- d.addLine("allowWhenScreenOff", allowWhenScreenOff, to.allowWhenScreenOff);
- }
- if (allowWhenScreenOn != to.allowWhenScreenOn) {
- d.addLine("allowWhenScreenOn", allowWhenScreenOn, to.allowWhenScreenOn);
- }
if (suppressedVisualEffects != to.suppressedVisualEffects) {
d.addLine("suppressedVisualEffects", suppressedVisualEffects,
to.suppressedVisualEffects);
@@ -404,8 +392,6 @@
&& other.allowMessagesFrom == allowMessagesFrom
&& other.allowReminders == allowReminders
&& other.allowEvents == allowEvents
- && other.allowWhenScreenOff == allowWhenScreenOff
- && other.allowWhenScreenOn == allowWhenScreenOn
&& other.user == user
&& Objects.equals(other.automaticRules, automaticRules)
&& Objects.equals(other.manualRule, manualRule)
@@ -418,7 +404,7 @@
return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
allowRepeatCallers, allowMessages,
allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
- allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule,
+ user, automaticRules, manualRule,
suppressedVisualEffects, areChannelsBypassingDnd);
}
@@ -472,6 +458,7 @@
final ZenModeConfig rt = new ZenModeConfig();
rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
+ boolean readSuppressedEffects = false;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
@@ -502,17 +489,33 @@
rt.allowCallsFrom = DEFAULT_SOURCE;
rt.allowMessagesFrom = DEFAULT_SOURCE;
}
- // continue to read even though we now have suppressedVisualEffects, in case
- // we need to revert to users' previous settings
- rt.allowWhenScreenOff =
- safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF);
- rt.allowWhenScreenOn =
- safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA,
DEFAULT_ALLOW_MEDIA);
rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM);
- } else if (DISALLOW_TAG.equals(tag)) {
+
+ // migrate old suppressed visual effects fields, if they still exist in the xml
+ Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
+ if (allowWhenScreenOff != null) {
+ readSuppressedEffects = true;
+ if (allowWhenScreenOff) {
+ rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
+ | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ }
+ }
+ Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
+ if (allowWhenScreenOn != null) {
+ readSuppressedEffects = true;
+ if (allowWhenScreenOn) {
+ rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
+ }
+ }
+ if (readSuppressedEffects) {
+ Slog.d(TAG, "Migrated visual effects to " + rt.suppressedVisualEffects);
+ }
+ } else if (DISALLOW_TAG.equals(tag) && !readSuppressedEffects) {
+ // only read from suppressed visual effects field if we haven't just migrated
+ // the values from allowOn/allowOff, lest we wipe out those settings
rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
} else if (MANUAL_TAG.equals(tag)) {
@@ -552,8 +555,6 @@
out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
- out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
- out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
@@ -673,6 +674,12 @@
return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
}
+ private static Boolean unsafeBoolean(XmlPullParser parser, String att) {
+ final String val = parser.getAttributeValue(null, att);
+ if (TextUtils.isEmpty(val)) return null;
+ return Boolean.parseBoolean(val);
+ }
+
private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
final String val = parser.getAttributeValue(null, att);
return safeBoolean(val, defValue);
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index f7a41ff..18cc10f 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -34,6 +34,7 @@
import android.graphics.RectF;
import android.graphics.TemporaryBuffer;
import android.text.GraphicsOperations;
+import android.text.MeasuredParagraph;
import android.text.PrecomputedText;
import android.text.SpannableString;
import android.text.SpannedString;
@@ -500,21 +501,31 @@
((GraphicsOperations) text).drawTextRun(this, start, end,
contextStart, contextEnd, x, y, isRtl, paint);
} else {
+ if (text instanceof PrecomputedText) {
+ final PrecomputedText pt = (PrecomputedText) text;
+ final int paraIndex = pt.findParaIndex(start);
+ if (end <= pt.getParagraphEnd(paraIndex)) {
+ final int paraStart = pt.getParagraphStart(paraIndex);
+ final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
+ // Only support if the target is in the same paragraph.
+ nDrawTextRun(mNativeCanvasWrapper,
+ mp.getChars(),
+ start - paraStart,
+ end - start,
+ contextStart - paraStart,
+ contextEnd - contextStart,
+ x, y, isRtl, paint.getNativeInstance(),
+ mp.getNativePtr());
+ return;
+ }
+ }
int contextLen = contextEnd - contextStart;
int len = end - start;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- long measuredTextPtr = 0;
- if (text instanceof PrecomputedText) {
- PrecomputedText mt = (PrecomputedText) text;
- int paraIndex = mt.findParaIndex(start);
- if (end <= mt.getParagraphEnd(paraIndex)) {
- // Only support if the target is in the same paragraph.
- measuredTextPtr = mt.getMeasuredParagraph(paraIndex).getNativePtr();
- }
- }
nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
- 0, contextLen, x, y, isRtl, paint.getNativeInstance(), measuredTextPtr);
+ 0, contextLen, x, y, isRtl, paint.getNativeInstance(),
+ 0 /* measured paragraph pointer */);
TemporaryBuffer.recycle(buf);
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7e54647..db34856 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -700,15 +700,9 @@
mIsCreating = false;
if (mSurfaceControl != null && !mSurfaceCreated) {
mSurface.release();
- // If we are not in the stopped state, then the destruction of the Surface
- // represents a visual change we need to display, and we should go ahead
- // and destroy the SurfaceControl. However if we are in the stopped state,
- // we can just leave the Surface around so it can be a part of animations,
- // and we let the life-time be tied to the parent surface.
- if (!mWindowStopped) {
- mSurfaceControl.destroy();
- mSurfaceControl = null;
- }
+
+ mSurfaceControl.destroy();
+ mSurfaceControl = null;
}
}
} catch (Exception ex) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 1c2e43e..6bacdfe 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -901,7 +901,7 @@
* Refreshes this info with the latest state of the view it represents, and request new
* data be added by the View.
*
- * @param extraDataKey A bitmask of the extra data requested. Data that must be requested
+ * @param extraDataKey The extra data requested. Data that must be requested
* with this mechanism is generally expensive to retrieve, so should only be
* requested when needed. See
* {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index 7558f8c..eada142 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -18,12 +18,15 @@
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Trace;
/**
* Shared singleton background thread for each process.
*/
public final class BackgroundThread extends HandlerThread {
+ private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
+ private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
private static BackgroundThread sInstance;
private static Handler sHandler;
@@ -35,7 +38,10 @@
if (sInstance == null) {
sInstance = new BackgroundThread();
sInstance.start();
- sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ final Looper looper = sInstance.getLooper();
+ looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ looper.setSlowLogThresholdMs(
+ SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index be83498..7703052 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4084,7 +4084,8 @@
boolean ensureStartClockTime(final long currentTime) {
final long ABOUT_ONE_YEAR = 365*24*60*60*1000L;
- if (currentTime > ABOUT_ONE_YEAR && mStartClockTime < (currentTime-ABOUT_ONE_YEAR)) {
+ if ((currentTime > ABOUT_ONE_YEAR && mStartClockTime < (currentTime-ABOUT_ONE_YEAR))
+ || (mStartClockTime > currentTime)) {
// If the start clock time has changed by more than a year, then presumably
// the previous time was completely bogus. So we are going to figure out a
// new time based on how much time has elapsed since we started counting.
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index eac9f64..cd80d53 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -23,6 +23,8 @@
import android.view.WindowManager;
import android.view.WindowManagerImpl;
+import java.lang.ref.WeakReference;
+
/**
* Context for decor views which can be seeded with pure application context and not depend on the
* activity, but still provide some of the facilities that Activity has,
@@ -35,9 +37,12 @@
private WindowManager mWindowManager;
private Resources mActivityResources;
- public DecorContext(Context context, Resources activityResources) {
+ private WeakReference<Context> mActivityContext;
+
+ public DecorContext(Context context, Context activityContext) {
super(context, null);
- mActivityResources = activityResources;
+ mActivityContext = new WeakReference<>(activityContext);
+ mActivityResources = activityContext.getResources();
}
void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -60,6 +65,13 @@
@Override
public Resources getResources() {
+ Context activityContext = mActivityContext.get();
+ // Attempt to update the local cached Resources from the activity context. If the activity
+ // is no longer around, return the old cached values.
+ if (activityContext != null) {
+ mActivityResources = activityContext.getResources();
+ }
+
return mActivityResources;
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 6bf2e0e..a186f1a 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1849,6 +1849,13 @@
}
@Override
+ public Resources getResources() {
+ // Make sure the Resources object is propogated from the Context since it can be updated in
+ // the Context object.
+ return getContext().getResources();
+ }
+
+ @Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 3af3e2a..a3c7a9e 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -344,7 +344,7 @@
if (dockedSide == DOCKED_LEFT) {
position += mInsets.left;
} else if (dockedSide == DOCKED_RIGHT) {
- position = mDisplayWidth - position - mInsets.right;
+ position = mDisplayWidth - position - mInsets.right - mDividerSize;
}
}
mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f090aca..64a8f9f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2298,7 +2298,7 @@
if (applicationContext == null) {
context = getContext();
} else {
- context = new DecorContext(applicationContext, getContext().getResources());
+ context = new DecorContext(applicationContext, getContext());
if (mTheme != -1) {
context.setTheme(mTheme);
}
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 5729b53..9048f87 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -77,7 +77,6 @@
int otherViews = 0;
int notGoneChildren = 0;
- View lastNotGoneChild = null;
for (int i = 0; i < N; i++) {
View c = getChildAt(i);
if (c instanceof TextView) {
@@ -87,7 +86,6 @@
}
if (c.getVisibility() != GONE) {
notGoneChildren++;
- lastNotGoneChild = c;
}
}
@@ -107,11 +105,8 @@
}
}
}
- boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
- boolean singleChildCentered = notGoneChildren == 1 && centerAligned;
- boolean needsRegularMeasurement = notGoneChildren > 1 || singleChildCentered;
- if (needsRegularMeasurement && needRebuild) {
+ if (needRebuild) {
rebuildMeasureOrder(textViews, otherViews);
}
@@ -123,7 +118,7 @@
int usedWidth = 0;
int measuredChildren = 0;
- for (int i = 0; i < N && needsRegularMeasurement; i++) {
+ for (int i = 0; i < N; i++) {
// Measure shortest children first. To avoid measuring twice, we approximate by looking
// at the text length.
View c;
@@ -156,25 +151,6 @@
measuredChildren++;
}
- // Make sure to measure the last child full-width if we didn't use up the entire width,
- // or we didn't measure yet because there's just one child.
- if (lastNotGoneChild != null && !centerAligned && (constrained && usedWidth < innerWidth
- || notGoneChildren == 1)) {
- MarginLayoutParams lp = (MarginLayoutParams) lastNotGoneChild.getLayoutParams();
- if (notGoneChildren > 1) {
- // Need to make room, since we already measured this once.
- usedWidth -= lastNotGoneChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
- }
-
- int originalWidth = lp.width;
- lp.width = LayoutParams.MATCH_PARENT;
- measureChildWithMargins(lastNotGoneChild, widthMeasureSpec, usedWidth,
- heightMeasureSpec, 0 /* usedHeight */);
- lp.width = originalWidth;
-
- usedWidth += lastNotGoneChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
- }
-
mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft;
setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
diff --git a/core/res/res/drawable/ic_dnd_block_notifications.xml b/core/res/res/drawable/ic_dnd_block_notifications.xml
new file mode 100644
index 0000000..4983614
--- /dev/null
+++ b/core/res/res/drawable/ic_dnd_block_notifications.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="40dp"
+ android:viewportWidth="40.0"
+ android:viewportHeight="40.0">
+ <path
+ android:pathData="M34,20H2c-1.1,0 -2,-0.9 -2,-2V6c0,-1.1 0.9,-2 2,-2h32c1.1,0 2,0.9 2,2v12C36,19.1 35.1,20 34,20z"
+ android:fillColor="#FBBC04"/>
+ <path
+ android:pathData="M4.63,10L4.63,10c-0.83,0 -1.5,-0.67 -1.5,-1.5v0C3.12,7.67 3.8,7 4.62,7h0c0.82,0 1.5,0.67 1.5,1.5v0C6.12,9.33 5.45,10 4.63,10z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M8.62,7.5h9.5v2h-9.5z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M3.12,15h24v2h-24z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M3.12,12h17.5v2h-17.5z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M33.59,6.41m-5.78,0a5.78,5.78 0,1 1,11.56 0a5.78,5.78 0,1 1,-11.56 0"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M33.5,0C29.91,0 27,2.91 27,6.5s2.91,6.5 6.5,6.5S40,10.09 40,6.5S37.09,0 33.5,0zM33.5,11.7c-2.87,0 -5.2,-2.33 -5.2,-5.2s2.33,-5.2 5.2,-5.2s5.2,2.33 5.2,5.2S36.37,11.7 33.5,11.7z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M30.25,5.85h6.5v1.3h-6.5z"
+ android:fillColor="#4285F4"/>
+</vector>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5c80d4d2..780cda2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1836,12 +1836,16 @@
<string name="db_default_sync_mode" translatable="false">FULL</string>
<!-- The database synchronization mode when using Write-Ahead Logging.
- FULL is safest and preserves durability at the cost of extra fsyncs.
- NORMAL sacrifices durability in WAL mode because syncs are only performed before
- and after checkpoint operations. If checkpoints are infrequent and power loss
- occurs, then committed transactions could be lost and applications might break.
+ From https://www.sqlite.org/pragma.html#pragma_synchronous:
+ WAL mode is safe from corruption with synchronous=NORMAL, and probably DELETE mode is safe
+ too on modern filesystems. WAL mode is always consistent with synchronous=NORMAL, but WAL
+ mode does lose durability. A transaction committed in WAL mode with
+ synchronous=NORMAL might roll back following a power loss or system crash.
+ Transactions are durable across application crashes regardless of the synchronous setting
+ or journal mode. The synchronous=NORMAL setting is a good choice for most applications
+ running in WAL mode.
Choices are: FULL, NORMAL, OFF. -->
- <string name="db_wal_sync_mode" translatable="false">FULL</string>
+ <string name="db_wal_sync_mode" translatable="false">NORMAL</string>
<!-- The Write-Ahead Log auto-checkpoint interval in database pages (typically 1 to 4KB).
The log is checkpointed automatically whenever it exceeds this many pages.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 45814ea..c7b65ef 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -207,6 +207,9 @@
<!-- The bottom padding for the notification header -->
<dimen name="notification_header_padding_bottom">16dp</dimen>
+ <!-- The margin at the top of the notification header when dozing. -->
+ <dimen name="notification_header_margin_top_ambient">3dp</dimen>
+
<!-- The margin at the bottom of the notification header. -->
<dimen name="notification_header_margin_bottom">0dp</dimen>
@@ -629,7 +632,7 @@
<!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
<dimen name="notification_media_image_max_width_low_ram">100dp</dimen>
<!-- The size of the right icon image when on low ram -->
- <dimen name="notification_right_icon_size_low_ram">38dp</dimen>
+ <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
<dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2d97db0..67b3c92 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -1309,6 +1309,7 @@
</style>
<style name="Notification.Header.Ambient">
+ <item name="layout_marginTop">@dimen/notification_header_margin_top_ambient</item>
<item name="gravity">top|center_horizontal</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5298e5b..c3ab1a6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2555,6 +2555,7 @@
<java-symbol type="drawable" name="ic_storage_48dp" />
<java-symbol type="drawable" name="ic_usb_48dp" />
<java-symbol type="drawable" name="ic_zen_24dp" />
+ <java-symbol type="drawable" name="ic_dnd_block_notifications" />
<!-- Floating toolbar -->
<java-symbol type="id" name="floating_toolbar_menu_item_image" />
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 3a71851..ba8173e 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -19,7 +19,7 @@
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
<zen version="7">
- <allow alarms="true" media="true" system="false" calls="false" messages="false"
+ <allow alarms="true" media="true" system="false" calls="false" callsFrom="2" messages="false"
reminders="false" events="false" />
<!-- all visual effects that exist as of P -->
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
index ff3452c..0fd0136 100644
--- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
@@ -28,7 +28,6 @@
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
@@ -40,7 +39,6 @@
import java.util.Objects;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class CursorAnchorInfoTest {
private static final float EPSILON = 0.0000001f;
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 52df338..9edbf3e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,7 +26,6 @@
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.LargeTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
@@ -37,7 +36,6 @@
import org.junit.runner.RunWith;
@LargeTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class InputMethodInfoTest {
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
index ff71c00..8df1848 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -30,7 +29,6 @@
import java.util.ArrayList;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class InputMethodSubtypeArrayTest {
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
index 2c85ce7..c76359e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -35,7 +34,6 @@
import java.util.Objects;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class InputMethodSubtypeTest {
diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
index c7b5933..8c96b58 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
@@ -23,7 +23,6 @@
import android.graphics.RectF;
import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
@@ -34,7 +33,6 @@
import java.util.Objects;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class SparseRectFArrayTest {
// A test data for {@link SparseRectFArray}. null represents the gap of indices.
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 0429d99..13d6749 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -23,7 +23,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodInfo;
@@ -41,7 +40,6 @@
import java.util.List;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class InputMethodSubtypeSwitchingControllerTest {
private static final String DUMMY_PACKAGE_NAME = "dummy package name";
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index a49e203..3064afa 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -37,7 +37,6 @@
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -54,7 +53,6 @@
import java.util.Objects;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class InputMethodUtilsTest {
private static final boolean IS_AUX = true;
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
index 1269ffd..549511a 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import android.os.LocaleList;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -30,7 +29,6 @@
import java.util.Locale;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class LocaleUtilsTest {
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index bf91a16..6d5276f 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -118,7 +118,7 @@
@Test
public void installPlatformSignedFrameworkOverlayAndUpdate() throws Exception {
- assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectAppResource"));
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectFrameworkResource"));
installPackage("OverlayHostTests_FrameworkOverlayV1.apk");
setOverlayEnabled(FRAMEWORK_OVERLAY_PACKAGE_NAME, true);
@@ -138,6 +138,27 @@
"expectFrameworkOverlayV2Resource"));
}
+ @Test
+ public void enabledFrameworkOverlayMustAffectNewlyInstalledPackage() throws Exception {
+ try {
+ setPackageEnabled(DEVICE_TEST_PKG, false);
+
+ installPackage("OverlayHostTests_FrameworkOverlayV1.apk");
+ setOverlayEnabled(FRAMEWORK_OVERLAY_PACKAGE_NAME, true);
+ assertTrue(overlayManagerContainsPackage(FRAMEWORK_OVERLAY_PACKAGE_NAME));
+
+ setPackageEnabled(DEVICE_TEST_PKG, true);
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+ "expectFrameworkOverlayV1Resource"));
+ } finally {
+ setPackageEnabled(DEVICE_TEST_PKG, true);
+ }
+ }
+
+ private void setPackageEnabled(String pkg, boolean enabled) throws Exception {
+ getDevice().executeShellCommand("cmd package " + (enabled ? "enable " : "disable ") + pkg);
+ }
+
private void setOverlayEnabled(String pkg, boolean enabled) throws Exception {
getDevice().executeShellCommand("cmd overlay " + (enabled ? "enable " : "disable ") + pkg);
}
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index d46bb37..a174d77 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -61,7 +61,7 @@
}
@Test
- public void expectFrameworkOverlayResource() throws Exception {
+ public void expectFrameworkResource() throws Exception {
assertEquals("OK", mResources.getString(android.R.string.ok));
}
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 71ee6c2..97130f1 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,6 +22,7 @@
import android.annotation.Size;
import android.graphics.Canvas.VertexMode;
import android.text.GraphicsOperations;
+import android.text.MeasuredParagraph;
import android.text.PrecomputedText;
import android.text.SpannableString;
import android.text.SpannedString;
@@ -486,21 +487,31 @@
((GraphicsOperations) text).drawTextRun(this, start, end,
contextStart, contextEnd, x, y, isRtl, paint);
} else {
+ if (text instanceof PrecomputedText) {
+ final PrecomputedText pt = (PrecomputedText) text;
+ final int paraIndex = pt.findParaIndex(start);
+ if (end <= pt.getParagraphEnd(paraIndex)) {
+ final int paraStart = pt.getParagraphStart(paraIndex);
+ final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
+ // Only support the text in the same paragraph.
+ nDrawTextRun(mNativeCanvasWrapper,
+ mp.getChars(),
+ start - paraStart,
+ end - start,
+ contextStart - paraStart,
+ contextEnd - contextStart,
+ x, y, isRtl, paint.getNativeInstance(),
+ mp.getNativePtr());
+ return;
+ }
+ }
int contextLen = contextEnd - contextStart;
int len = end - start;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- long measuredTextPtr = 0;
- if (text instanceof PrecomputedText) {
- PrecomputedText mt = (PrecomputedText) text;
- int paraIndex = mt.findParaIndex(start);
- if (end <= mt.getParagraphEnd(paraIndex)) {
- // Only suppor the text in the same paragraph.
- measuredTextPtr = mt.getMeasuredParagraph(paraIndex).getNativePtr();
- }
- }
nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
- 0, contextLen, x, y, isRtl, paint.getNativeInstance(), measuredTextPtr);
+ 0, contextLen, x, y, isRtl, paint.getNativeInstance(),
+ 0 /* measured paragraph pointer */);
TemporaryBuffer.recycle(buf);
}
}
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index be7d663..569de76 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -40,7 +40,6 @@
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
- SkSafeUnref(mColorFilter);
setTransform(nullptr);
mRenderState.unregisterDeferredLayerUpdater(this);
destroyLayer();
@@ -67,8 +66,11 @@
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
mAlpha = PaintUtils::getAlphaDirect(paint);
mMode = PaintUtils::getBlendModeDirect(paint);
- SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
- SkRefCnt_SafeAssign(mColorFilter, colorFilter);
+ if (paint) {
+ mColorFilter = paint->refColorFilter();
+ } else {
+ mColorFilter.reset();
+ }
}
void DeferredLayerUpdater::apply() {
@@ -143,7 +145,7 @@
#endif
mSurfaceTexture->getTransformMatrix(transform);
- updateLayer(forceFilter, transform);
+ updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace());
}
}
@@ -153,17 +155,19 @@
Layer::Api::OpenGL, Layer::Api::Vulkan);
static const mat4 identityMatrix;
- updateLayer(false, identityMatrix.data);
+ updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN);
VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
vkLayer->updateTexture();
}
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) {
+void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform,
+ android_dataspace dataspace) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
mLayer->getTexTransform().load(textureTransform);
+ mLayer->setDataSpace(dataspace);
}
void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 9dc029f..fe3ee7a 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -20,6 +20,7 @@
#include <SkMatrix.h>
#include <cutils/compiler.h>
#include <gui/GLConsumer.h>
+#include <system/graphics.h>
#include <utils/StrongPointer.h>
#include <GLES2/gl2.h>
@@ -41,7 +42,7 @@
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth,
- uint32_t layerHeight, SkColorFilter* colorFilter, int alpha,
+ uint32_t layerHeight, sk_sp<SkColorFilter> colorFilter, int alpha,
SkBlendMode mode, bool blend)>
CreateLayerFn;
ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
@@ -96,7 +97,7 @@
void detachSurfaceTexture();
- void updateLayer(bool forceFilter, const float* textureTransform);
+ void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace);
void destroyLayer();
@@ -109,7 +110,7 @@
int mWidth = 0;
int mHeight = 0;
bool mBlend = false;
- SkColorFilter* mColorFilter = nullptr;
+ sk_sp<SkColorFilter> mColorFilter;
int mAlpha = 255;
SkBlendMode mMode = SkBlendMode::kSrcOver;
sp<GLConsumer> mSurfaceTexture;
diff --git a/libs/hwui/EglReadback.cpp b/libs/hwui/EglReadback.cpp
index a836afe..2d5367b 100644
--- a/libs/hwui/EglReadback.cpp
+++ b/libs/hwui/EglReadback.cpp
@@ -58,7 +58,7 @@
CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
Matrix4& texTransform, const Rect& srcRect,
SkBitmap* bitmap) {
- mRenderThread.eglManager().initialize();
+ mRenderThread.requireGlContext();
// TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
// GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
// to be able to properly sample from the buffer.
diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp
index 32a3014..42ae29d 100644
--- a/libs/hwui/GlLayer.cpp
+++ b/libs/hwui/GlLayer.cpp
@@ -32,7 +32,7 @@
namespace uirenderer {
GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
: Layer(renderState, Api::OpenGL, colorFilter, alpha, mode)
, caches(Caches::getInstance())
, texture(caches) {
diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h
index 1b09191..28749a0 100644
--- a/libs/hwui/GlLayer.h
+++ b/libs/hwui/GlLayer.h
@@ -32,7 +32,7 @@
class GlLayer : public Layer {
public:
GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend);
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend);
virtual ~GlLayer();
uint32_t getWidth() const override { return texture.mWidth; }
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 86950d5..fb8f033 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,34 +18,58 @@
#include "renderstate/RenderState.h"
-#include <SkColorFilter.h>
+#include <SkToSRGBColorFilter.h>
namespace android {
namespace uirenderer {
-Layer::Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
+Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter, int alpha,
SkBlendMode mode)
: GpuMemoryTracker(GpuObjectType::Layer)
, mRenderState(renderState)
, mApi(api)
- , colorFilter(nullptr)
+ , mColorFilter(colorFilter)
, alpha(alpha)
, mode(mode) {
// TODO: This is a violation of Android's typical ref counting, but it
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
-
+ buildColorSpaceWithFilter();
renderState.registerLayer(this);
}
Layer::~Layer() {
- SkSafeUnref(colorFilter);
-
mRenderState.unregisterLayer(this);
}
-void Layer::setColorFilter(SkColorFilter* filter) {
- SkRefCnt_SafeAssign(colorFilter, filter);
+void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
+ if (filter != mColorFilter) {
+ mColorFilter = filter;
+ buildColorSpaceWithFilter();
+ }
+}
+
+void Layer::setDataSpace(android_dataspace dataspace) {
+ if (dataspace != mCurrentDataspace) {
+ mCurrentDataspace = dataspace;
+ buildColorSpaceWithFilter();
+ }
+}
+
+void Layer::buildColorSpaceWithFilter() {
+ sk_sp<SkColorFilter> colorSpaceFilter;
+ sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
+ if (colorSpace && !colorSpace->isSRGB()) {
+ colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+ }
+
+ if (mColorFilter && colorSpaceFilter) {
+ mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
+ } else if (colorSpaceFilter) {
+ mColorSpaceWithFilter = colorSpaceFilter;
+ } else {
+ mColorSpaceWithFilter = mColorFilter;
+ }
}
void Layer::postDecStrong() {
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 6921381..89bcddc 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -19,6 +19,8 @@
#include <GpuMemoryTracker.h>
#include <utils/RefBase.h>
+#include <SkColorFilter.h>
+#include <SkColorSpace.h>
#include <SkBlendMode.h>
#include <SkPaint.h>
@@ -72,9 +74,15 @@
inline SkBlendMode getMode() const { return mode; }
- inline SkColorFilter* getColorFilter() const { return colorFilter; }
+ inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
- void setColorFilter(SkColorFilter* filter);
+ void setColorFilter(sk_sp<SkColorFilter> filter);
+
+ void setDataSpace(android_dataspace dataspace);
+
+ void setColorSpace(sk_sp<SkColorSpace> colorSpace);
+
+ inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
inline mat4& getTexTransform() { return texTransform; }
@@ -87,18 +95,30 @@
void postDecStrong();
protected:
- Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
+ Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha,
SkBlendMode mode);
RenderState& mRenderState;
private:
+ void buildColorSpaceWithFilter();
+
Api mApi;
/**
* Color filter used to draw this layer. Optional.
*/
- SkColorFilter* colorFilter;
+ sk_sp<SkColorFilter> mColorFilter;
+
+ /**
+ * Colorspace of the contents of the layer. Optional.
+ */
+ android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
+
+ /**
+ * A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
+ */
+ sk_sp<SkColorFilter> mColorSpaceWithFilter;
/**
* Indicates raster data backing the layer is scaled, requiring filtration.
diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h
index f23f472..e9664d0 100644
--- a/libs/hwui/VkLayer.h
+++ b/libs/hwui/VkLayer.h
@@ -28,7 +28,7 @@
class VkLayer : public Layer {
public:
VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
: Layer(renderState, Api::Vulkan, colorFilter, alpha, mode)
, mWidth(layerWidth)
, mHeight(layerHeight)
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 896c01c..00ba7130 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -50,9 +50,13 @@
GlLayer* glLayer = static_cast<GlLayer*>(layer);
GrGLTextureInfo externalTexture;
externalTexture.fTarget = glLayer->getRenderTarget();
- SkASSERT(GL_RGBA == glLayer->getTexture().internalFormat());
- externalTexture.fFormat = GL_RGBA8;
externalTexture.fID = glLayer->getTextureId();
+ // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't
+ // expose that info we use it as our default. Further, given that we only use this texture
+ // as a source this will not impact how Skia uses the texture. The only potential affect
+ // this is anticipated to have is that for some format types if we are not bound as an OES
+ // texture we may get invalid results for SKP capture if we read back the texture.
+ externalTexture.fFormat = GL_RGBA8;
GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture);
layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
@@ -83,7 +87,7 @@
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
- paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
+ paint.setColorFilter(layer->getColorSpaceWithFilter());
const bool nonIdentityMatrix = !matrix.isIdentity();
if (nonIdentityMatrix) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index b7aa78b..cfcfd2b 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -158,7 +158,7 @@
}
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend) {
GlLayer* layer =
new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
layer->generateTexture();
@@ -166,7 +166,7 @@
}
DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
- mEglManager.initialize();
+ mRenderThread.requireGlContext();
return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
}
@@ -184,6 +184,7 @@
}
if (surface) {
+ mRenderThread.requireGlContext();
const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
}
@@ -221,24 +222,6 @@
#define FENCE_TIMEOUT 2000000000
-class AutoEglFence {
-public:
- AutoEglFence(EGLDisplay display) : mDisplay(display) {
- fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
- }
-
- ~AutoEglFence() {
- if (fence != EGL_NO_SYNC_KHR) {
- eglDestroySyncKHR(mDisplay, fence);
- }
- }
-
- EGLSyncKHR fence = EGL_NO_SYNC_KHR;
-
-private:
- EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
class AutoEglImage {
public:
AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
@@ -272,73 +255,102 @@
GLuint mTexture = 0;
};
-sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
- SkBitmap& skBitmap) {
- renderThread.eglManager().initialize();
+struct FormatInfo {
- sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext());
- const SkImageInfo& info = skBitmap.info();
PixelFormat pixelFormat;
GLint format, type;
bool isSupported = false;
+ bool valid = true;
+};
+static bool gpuSupportsHalfFloatTextures(renderthread::RenderThread& renderThread) {
+ static bool isSupported = renderThread.queue().runSync([&renderThread]() -> bool {
+ renderThread.requireGlContext();
+ sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext());
+ return grContext->colorTypeSupportedAsImage(kRGBA_F16_SkColorType);
+ });
+ return isSupported;
+}
+
+static FormatInfo determineFormat(renderthread::RenderThread& renderThread,
+ const SkBitmap& skBitmap) {
+ FormatInfo formatInfo;
// TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
- switch (info.colorType()) {
+ switch (skBitmap.info().colorType()) {
case kRGBA_8888_SkColorType:
- isSupported = true;
+ formatInfo.isSupported = true;
// ARGB_4444 is upconverted to RGBA_8888
case kARGB_4444_SkColorType:
- pixelFormat = PIXEL_FORMAT_RGBA_8888;
- format = GL_RGBA;
- type = GL_UNSIGNED_BYTE;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ formatInfo.format = GL_RGBA;
+ formatInfo.type = GL_UNSIGNED_BYTE;
break;
case kRGBA_F16_SkColorType:
- isSupported = grContext->colorTypeSupportedAsImage(kRGBA_F16_SkColorType);
- if (isSupported) {
- type = GL_HALF_FLOAT;
- pixelFormat = PIXEL_FORMAT_RGBA_FP16;
+ formatInfo.isSupported = gpuSupportsHalfFloatTextures(renderThread);
+ if (formatInfo.isSupported) {
+ formatInfo.type = GL_HALF_FLOAT;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
} else {
- type = GL_UNSIGNED_BYTE;
- pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
}
- format = GL_RGBA;
+ formatInfo.format = GL_RGBA;
break;
case kRGB_565_SkColorType:
- isSupported = true;
- pixelFormat = PIXEL_FORMAT_RGB_565;
- format = GL_RGB;
- type = GL_UNSIGNED_SHORT_5_6_5;
+ formatInfo.isSupported = true;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
+ formatInfo.format = GL_RGB;
+ formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
break;
case kGray_8_SkColorType:
- isSupported = true;
- pixelFormat = PIXEL_FORMAT_RGBA_8888;
- format = GL_LUMINANCE;
- type = GL_UNSIGNED_BYTE;
+ formatInfo.isSupported = true;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ formatInfo.format = GL_LUMINANCE;
+ formatInfo.type = GL_UNSIGNED_BYTE;
break;
default:
- ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
- return nullptr;
+ ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
+ formatInfo.valid = false;
}
+ return formatInfo;
+}
- SkBitmap bitmap;
- if (isSupported) {
- bitmap = skBitmap;
+static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
+ if (format.isSupported) {
+ return source;
} else {
+ SkBitmap bitmap;
+ const SkImageInfo& info = source.info();
bitmap.allocPixels(
SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
bitmap.eraseColor(0);
if (info.colorType() == kRGBA_F16_SkColorType) {
// Drawing RGBA_F16 onto ARGB_8888 is not supported
- skBitmap.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
- bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
+ source.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
+ bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
} else {
SkCanvas canvas(bitmap);
- canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
+ canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
}
+ return bitmap;
+ }
+}
+
+sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& thread,
+ const SkBitmap& sourceBitmap) {
+ ATRACE_CALL();
+
+ LOG_ALWAYS_FATAL_IF(thread.isCurrent(), "Must not be called on RenderThread");
+
+ FormatInfo format = determineFormat(thread, sourceBitmap);
+ if (!format.valid) {
+ return nullptr;
}
+ SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
sp<GraphicBuffer> buffer = new GraphicBuffer(
- info.width(), info.height(), pixelFormat,
+ static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
+ format.pixelFormat,
GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
GraphicBuffer::USAGE_SW_READ_NEVER,
std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) +
@@ -350,8 +362,11 @@
return nullptr;
}
- // upload the bitmap into a texture
- EGLDisplay display = eglGetCurrentDisplay();
+ EGLDisplay display = thread.queue().runSync([&]() -> EGLDisplay {
+ thread.requireGlContext();
+ return eglGetCurrentDisplay();
+ });
+
LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
uirenderer::renderthread::EglManager::eglErrorString());
// We use an EGLImage to access the content of the GraphicBuffer
@@ -363,35 +378,40 @@
uirenderer::renderthread::EglManager::eglErrorString());
return nullptr;
}
- AutoSkiaGlTexture glTexture;
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
- GL_CHECKPOINT(MODERATE);
- // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we provide.
- // But asynchronous in sense that driver may upload texture onto hardware buffer when we first
- // use it in drawing
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, info.width(), info.height(), format, type,
- bitmap.getPixels());
- GL_CHECKPOINT(MODERATE);
+ {
+ ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
+ EGLSyncKHR fence = thread.queue().runSync([&]() -> EGLSyncKHR {
+ thread.requireGlContext();
+ sk_sp<GrContext> grContext = sk_ref_sp(thread.getGrContext());
+ AutoSkiaGlTexture glTexture;
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+ GL_CHECKPOINT(MODERATE);
- // The fence is used to wait for the texture upload to finish
- // properly. We cannot rely on glFlush() and glFinish() as
- // some drivers completely ignore these API calls
- AutoEglFence autoFence(display);
- if (autoFence.fence == EGL_NO_SYNC_KHR) {
- LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError());
- return nullptr;
+ // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
+ // provide.
+ // But asynchronous in sense that driver may upload texture onto hardware buffer when we
+ // first
+ // use it in drawing
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format,
+ format.type, bitmap.getPixels());
+ GL_CHECKPOINT(MODERATE);
+
+ EGLSyncKHR uploadFence =
+ eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+ LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, "Could not create sync fence %#x",
+ eglGetError());
+ glFlush();
+ grContext->resetContext(kTextureBinding_GrGLBackendState);
+ return uploadFence;
+ });
+
+ EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
+ LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+ "Failed to wait for the fence %#x", eglGetError());
+
+ eglDestroySyncKHR(display, fence);
}
- // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
- // pipeline flush (similar to what a glFlush() would do.)
- EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence,
- EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
- if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
- LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError());
- return nullptr;
- }
-
- grContext->resetContext(kTextureBinding_GrGLBackendState);
return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 5e013b6..ef5d934 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -49,8 +49,10 @@
bool isContextReady() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+
+ // May be called by any thread except RenderThread.
static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
- SkBitmap& skBitmap);
+ const SkBitmap& skBitmap);
private:
renderthread::EglManager& mEglManager;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 9e73046..d66cba1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -147,6 +147,7 @@
GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
if (cachedContext.get() != currentContext) {
if (cachedContext.get()) {
+ ATRACE_NAME("flush layers (context changed)");
cachedContext->flush();
}
cachedContext.reset(SkSafeRef(currentContext));
@@ -155,6 +156,7 @@
}
if (cachedContext.get()) {
+ ATRACE_NAME("flush layers");
cachedContext->flush();
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 6530074..5825060 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -115,7 +115,8 @@
}
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
+ bool blend) {
return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 95ca8d9..a36dae4 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -552,7 +552,7 @@
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
- thread.eglManager().destroy();
+ thread.destroyGlContext();
thread.vulkanManager().destroy();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 6e239e3..cd21822 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,27 +16,19 @@
#include "EglManager.h"
-#include <string>
-
#include <cutils/properties.h>
#include <log/log.h>
+#include <utils/Trace.h>
#include "utils/StringUtils.h"
-#include "Caches.h"
#include "DeviceInfo.h"
#include "Frame.h"
#include "Properties.h"
-#include "RenderThread.h"
-#include "Texture.h"
-#include "renderstate/RenderState.h"
#include <EGL/eglext.h>
-#include <GrContextOptions.h>
-#include <gl/GrGLInterface.h>
-#ifdef HWUI_GLES_WRAP_ENABLED
-#include "debug/GlesDriver.h"
-#endif
+#include <string>
+#include <vector>
#define GLES_VERSION 2
@@ -83,17 +75,21 @@
bool glColorSpace = false;
bool scRGB = false;
bool contextPriority = false;
+ bool surfacelessContext = false;
} EglExtensions;
-EglManager::EglManager(RenderThread& thread)
- : mRenderThread(thread)
- , mEglDisplay(EGL_NO_DISPLAY)
+EglManager::EglManager()
+ : mEglDisplay(EGL_NO_DISPLAY)
, mEglConfig(nullptr)
, mEglConfigWideGamut(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
, mCurrentSurface(EGL_NO_SURFACE) {}
+EglManager::~EglManager() {
+ destroy();
+}
+
void EglManager::initialize() {
if (hasEglContext()) return;
@@ -126,26 +122,8 @@
loadConfigs();
createContext();
createPBufferSurface();
- makeCurrent(mPBufferSurface);
+ makeCurrent(mPBufferSurface, nullptr, /* force */ true);
DeviceInfo::initialize();
- mRenderThread.renderState().onGLContextCreated();
-
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
-#ifdef HWUI_GLES_WRAP_ENABLED
- debug::GlesDriver* driver = debug::GlesDriver::get();
- sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
-#else
- sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
-#endif
- LOG_ALWAYS_FATAL_IF(!glInterface.get());
-
- GrContextOptions options;
- options.fDisableDistanceFieldPaths = true;
- mRenderThread.cacheManager().configureContext(&options);
- sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
- LOG_ALWAYS_FATAL_IF(!grContext.get());
- mRenderThread.setGrContext(grContext);
- }
}
void EglManager::initExtensions() {
@@ -170,6 +148,7 @@
EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
#endif
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
+ EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
}
bool EglManager::hasEglContext() {
@@ -195,7 +174,7 @@
EGL_CONFIG_CAVEAT,
EGL_NONE,
EGL_STENCIL_SIZE,
- Stencil::getStencilSize(),
+ STENCIL_BUFFER_SIZE,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | swapBehavior,
EGL_NONE};
@@ -232,7 +211,7 @@
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
- Stencil::getStencilSize(),
+ STENCIL_BUFFER_SIZE,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | swapBehavior,
EGL_NONE};
@@ -269,14 +248,14 @@
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
"usePBufferSurface() called on uninitialized GlobalContext!");
- if (mPBufferSurface == EGL_NO_SURFACE) {
+ if (mPBufferSurface == EGL_NO_SURFACE && !EglExtensions.surfacelessContext) {
EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
}
}
EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) {
- initialize();
+ LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB &&
EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext;
@@ -350,10 +329,10 @@
void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
- mRenderThread.setGrContext(nullptr);
- mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
- eglDestroySurface(mEglDisplay, mPBufferSurface);
+ if (mPBufferSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mPBufferSurface);
+ }
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEglDisplay);
eglReleaseThread();
@@ -364,8 +343,8 @@
mCurrentSurface = EGL_NO_SURFACE;
}
-bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
- if (isCurrent(surface)) return false;
+bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut, bool force) {
+ if (!force && isCurrent(surface)) return false;
if (surface == EGL_NO_SURFACE) {
// Ensure we always have a valid surface & context
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index ef9effb..ca6d1b8 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -33,8 +33,12 @@
// and EGLConfig, which are re-used by CanvasContext
class EglManager {
public:
+ explicit EglManager();
+
+ ~EglManager();
+
static const char* eglErrorString();
- // Returns true on success, false on failure
+
void initialize();
bool hasEglContext();
@@ -46,7 +50,7 @@
bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
// Returns true if the current surface changed, false if it was already current
- bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr);
+ bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr, bool force = false);
Frame beginFrame(EGLSurface surface);
void damageFrame(const Frame& frame, const SkRect& dirty);
// If this returns true it is mandatory that swapBuffers is called
@@ -61,10 +65,6 @@
void fence();
private:
- friend class RenderThread;
- explicit EglManager(RenderThread& thread);
- // EglContext is never destroyed, method is purposely not implemented
- ~EglManager();
void initExtensions();
void createPBufferSurface();
@@ -72,8 +72,6 @@
void createContext();
EGLint queryBufferAge(EGLSurface surface);
- RenderThread& mRenderThread;
-
EGLDisplay mEglDisplay;
EGLConfig mEglConfig;
EGLConfig mEglConfigWideGamut;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 4b948f8..0caf59b 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -21,6 +21,7 @@
#include "Properties.h"
#include "Readback.h"
#include "Rect.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/VectorDrawableAtlas.h"
#include "renderstate/RenderState.h"
#include "renderthread/CanvasContext.h"
@@ -323,7 +324,13 @@
sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
auto& thread = RenderThread::getInstance();
- return thread.queue().runSync([&]() -> auto { return thread.allocateHardwareBitmap(bitmap); });
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(thread, bitmap);
+ } else {
+ return thread.queue().runSync([&]() -> auto {
+ return thread.allocateHardwareBitmap(bitmap);
+ });
+ }
}
int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 5e067da..3eaf43b 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -24,12 +24,19 @@
#include "hwui/Bitmap.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaOpenGLReadback.h"
-#include "pipeline/skia/SkiaVulkanReadback.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
+#include "pipeline/skia/SkiaVulkanReadback.h"
#include "renderstate/RenderState.h"
#include "utils/FatVector.h"
#include "utils/TimeUtils.h"
+#ifdef HWUI_GLES_WRAP_ENABLED
+#include "debug/GlesDriver.h"
+#endif
+
+#include <GrContextOptions.h>
+#include <gl/GrGLInterface.h>
+
#include <gui/DisplayEventReceiver.h>
#include <sys/resource.h>
#include <utils/Condition.h>
@@ -91,14 +98,11 @@
DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- mRenderThread->queue().postDelayed(16_ms, [this]() {
- mRenderThread->drainDisplayEventQueue();
- });
+ mRenderThread->queue().postDelayed(16_ms,
+ [this]() { mRenderThread->drainDisplayEventQueue(); });
}
- virtual nsecs_t latestVsyncEvent() override {
- return systemTime(CLOCK_MONOTONIC);
- }
+ virtual nsecs_t latestVsyncEvent() override { return systemTime(CLOCK_MONOTONIC); }
private:
RenderThread* mRenderThread;
@@ -145,13 +149,13 @@
auto receiver = std::make_unique<DisplayEventReceiver>();
status_t status = receiver->initCheck();
LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "Initialization of DisplayEventReceiver "
- "failed with status: %d",
- status);
+ "Initialization of DisplayEventReceiver "
+ "failed with status: %d",
+ status);
// Register the FD
mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
- RenderThread::displayEventReceiverCallback, this);
+ RenderThread::displayEventReceiverCallback, this);
mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver));
} else {
mVsyncSource = new DummyVsyncSource(this);
@@ -163,12 +167,43 @@
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
mTimeLord.setFrameInterval(frameIntervalNanos);
initializeDisplayEventReceiver();
- mEglManager = new EglManager(*this);
+ mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager(*this);
mCacheManager = new CacheManager(mDisplayInfo);
}
+void RenderThread::requireGlContext() {
+ if (mEglManager->hasEglContext()) {
+ return;
+ }
+ mEglManager->initialize();
+ renderState().onGLContextCreated();
+
+#ifdef HWUI_GLES_WRAP_ENABLED
+ debug::GlesDriver* driver = debug::GlesDriver::get();
+ sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
+#else
+ sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+#endif
+ LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+ GrContextOptions options;
+ options.fDisableDistanceFieldPaths = true;
+ cacheManager().configureContext(&options);
+ sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
+ LOG_ALWAYS_FATAL_IF(!grContext.get());
+ setGrContext(grContext);
+}
+
+void RenderThread::destroyGlContext() {
+ if (mEglManager->hasEglContext()) {
+ setGrContext(nullptr);
+ renderState().onGLContextDestroyed();
+ mEglManager->destroy();
+ }
+}
+
void RenderThread::dumpGraphicsMemory(int fd) {
globalProfileData()->dump(fd);
@@ -332,8 +367,6 @@
sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
- case RenderPipelineType::SkiaGL:
- return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
case RenderPipelineType::SkiaVulkan:
return skiapipeline::SkiaVulkanPipeline::allocateHardwareBitmap(*this, skBitmap);
default:
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 689f518..4a1fd9e 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -103,6 +103,9 @@
sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
void dumpGraphicsMemory(int fd);
+ void requireGlContext();
+ void destroyGlContext();
+
/**
* isCurrent provides a way to query, if the caller is running on
* the render thread.
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 16c5afd..a7ac930 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -72,7 +72,7 @@
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- layerUpdater->updateLayer(true, Matrix4::identity().data);
+ layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN);
if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
static_cast<GlLayer*>(layerUpdater->backingLayer())
->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
@@ -140,7 +140,7 @@
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
renderThread.vulkanManager().initialize();
} else {
- renderThread.eglManager().initialize();
+ renderThread.requireGlContext();
}
rtCallback(renderThread);
@@ -149,7 +149,7 @@
renderThread.vulkanManager().destroy();
} else {
renderThread.renderState().flush(Caches::FlushMode::Full);
- renderThread.eglManager().destroy();
+ renderThread.destroyGlContext();
}
}
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index b8b5050..f29830f 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -44,7 +44,7 @@
// push the deferred updates to the layer
Matrix4 scaledMatrix;
scaledMatrix.loadScale(0.5, 0.5, 0.0);
- layerUpdater->updateLayer(true, scaledMatrix.data);
+ layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN);
if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index 9bfb082..08b9679 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -39,7 +39,7 @@
// current thread can spoof being a GPU thread
static void destroyEglContext() {
if (TestUtils::isRenderThreadRunning()) {
- TestUtils::runOnRenderThread([](RenderThread& thread) { thread.eglManager().destroy(); });
+ TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
}
}
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index c2af867..75740e8 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,6 +16,8 @@
#include "Color.h"
+
+#include <utils/Log.h>
#include <cmath>
namespace android {
@@ -53,5 +55,57 @@
return false;
}
+sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
+
+ SkColorSpace::Gamut gamut;
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ gamut = SkColorSpace::kSRGB_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ gamut = SkColorSpace::kRec2020_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ gamut = SkColorSpace::kDCIP3_D65_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ gamut = SkColorSpace::kAdobeRGB_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ return nullptr;
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ default:
+ ALOGW("Unsupported Gamut: %d", dataspace);
+ return nullptr;
+ }
+
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut);
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
+ return nullptr;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ default:
+ ALOGW("Unsupported Gamma: %d", dataspace);
+ return nullptr;
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 7ac0d96..2bec1f5 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -17,6 +17,7 @@
#define COLOR_H
#include <math.h>
+#include <system/graphics.h>
#include <SkColor.h>
#include <SkColorSpace.h>
@@ -111,6 +112,8 @@
// approximated with the native sRGB transfer function. This method
// returns true for sRGB, gamma 2.2 and Display P3 for instance
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
+
+sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index 14b2e41..44bc97a 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -12,9 +12,8 @@
// 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.
-
-cc_library {
- name: "libprotoutil",
+cc_defaults {
+ name: "libprotoutil_defaults",
cflags: [
"-Wall",
@@ -30,8 +29,6 @@
"src/protobuf.cpp",
],
- export_include_dirs: ["include"],
-
shared_libs: [
"libbase",
"libcutils",
@@ -39,15 +36,24 @@
],
}
+cc_library {
+ name: "libprotoutil",
+
+ defaults: ["libprotoutil_defaults"],
+
+ export_include_dirs: ["include"],
+}
+
cc_test {
name: "libprotoutil_test",
+ defaults: ["libprotoutil_defaults"],
+
+ local_include_dirs: ["include"],
+
srcs: ["tests/*"],
shared_libs: [
- "libbase",
- "libcutils",
- "libprotoutil",
"libprotobuf-cpp-full",
],
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index ad90893..ad76559 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -105,7 +105,7 @@
/**
* Starts a sub-message write session.
* Returns a token of this write session.
- * Must call end(token) when finish write this sub-message.
+ * Must call end(token) exactly once when finish write this sub-message.
*/
uint64_t start(uint64_t fieldId);
void end(uint64_t token);
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 0d429e0..ff3fad6 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -244,12 +244,14 @@
{
if (token != mExpectedObjectToken) {
ALOGE("Unexpected token: 0x%" PRIx64 ", should be 0x%" PRIx64, token, mExpectedObjectToken);
+ mDepth = UINT32_C(-1); // make depth invalid
return;
}
uint32_t depth = getDepthFromToken(token);
if (depth != (mDepth & 0x01ff)) {
ALOGE("Unexpected depth: %" PRIu32 ", should be %" PRIu32, depth, mDepth);
+ mDepth = UINT32_C(-1); // make depth invalid
return;
}
mDepth--;
@@ -282,7 +284,7 @@
ProtoOutputStream::compact() {
if (mCompact) return true;
if (mDepth != 0) {
- ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing calls to end.", mDepth);
+ ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing or extra calls to end.", mDepth);
return false;
}
// record the size of the original buffer.
@@ -425,7 +427,7 @@
{
if (!compact()) {
ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
- // TODO: handle this error
+ return 0;
}
return mBuffer.size();
}
@@ -449,7 +451,7 @@
{
if (!compact()) {
ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
- // TODO: handle this error
+ mBuffer.clear();
}
return mBuffer.begin();
}
diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
index 5c25787..27ee13d 100644
--- a/libs/protoutil/tests/ProtoOutputStream_test.cpp
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -189,4 +189,40 @@
EXPECT_FALSE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 234.34));
EXPECT_FALSE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, 18.73f));
EXPECT_EQ(proto.size(), 0);
-}
\ No newline at end of file
+}
+
+TEST(ProtoOutputStreamTest, NoEndCalled) {
+ ProtoOutputStream proto;
+ proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+ // no proto.end called
+ EXPECT_NE(proto.bytesWritten(), 0);
+ EXPECT_EQ(proto.size(), 0);
+ EXPECT_EQ(proto.data().size(), 0);
+ EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
+
+
+TEST(ProtoOutputStreamTest, TwoEndCalled) {
+ ProtoOutputStream proto;
+ uint64_t token = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+ proto.end(token);
+ proto.end(token);
+ EXPECT_NE(proto.bytesWritten(), 0);
+ EXPECT_EQ(proto.size(), 0);
+ EXPECT_EQ(proto.data().size(), 0);
+ EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
+
+TEST(ProtoOutputStreamTest, NoStartCalled) {
+ ProtoOutputStream proto;
+ uint64_t wrongToken = UINT64_C(324536345);
+ // no proto.start called
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+ proto.end(wrongToken);
+ EXPECT_NE(proto.bytesWritten(), 0);
+ EXPECT_EQ(proto.size(), 0);
+ EXPECT_EQ(proto.data().size(), 0);
+ EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 7ac1529..3a0a58e 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -86,7 +86,7 @@
* <p>
* Once the app has a sessionId, it can construct a MediaCrypto object from the UUID and
* sessionId. The MediaCrypto object is registered with the MediaCodec in the
- * {@link MediaCodec.#configure} method to enable the codec to decrypt content.
+ * {@link MediaCodec#configure} method to enable the codec to decrypt content.
* <p>
* When the app has constructed {@link android.media.MediaExtractor},
* {@link android.media.MediaCodec} and {@link android.media.MediaCrypto} objects,
diff --git a/nfc-extras/Android.bp b/nfc-extras/Android.bp
new file mode 100644
index 0000000..cbacd48
--- /dev/null
+++ b/nfc-extras/Android.bp
@@ -0,0 +1,21 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+java_sdk_library {
+ name: "com.android.nfc_extras",
+ srcs: ["java/**/*.java"],
+ api_packages: ["com.android.nfc_extras"],
+}
diff --git a/nfc-extras/Android.mk b/nfc-extras/Android.mk
deleted file mode 100644
index 03de00c..0000000
--- a/nfc-extras/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.nfc_extras
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under,java)
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.nfc_extras-stubs-gen
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := $(call all-java-files-under,java)
-# This is to reference SdkConstant annotation; not part of this lib.
-LOCAL_DROIDDOC_SOURCE_PATH := frameworks/base/core/java/android/annotation
-LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.nfc_extras.stubs_intermediates/src
-LOCAL_DROIDDOC_OPTIONS:= \
- -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
- -stubpackages com.android.nfc_extras \
- -nodocs
-LOCAL_UNINSTALLABLE_MODULE := true
-include $(BUILD_DROIDDOC)
-com_android_nfc_extras_gen_stamp := $(full_target)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.nfc_extras.stubs
-# This is to reference SdkConstant annotation; not part of this lib.
-LOCAL_SRC_FILES := ../core/java/android/annotation/SdkConstant.java
-LOCAL_SDK_VERSION := current
-LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_nfc_extras_gen_stamp)
-com_android_nfc_extras_gen_stamp :=
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/nfc-extras/com.android.nfc_extras.xml b/nfc-extras/com.android.nfc_extras.xml
deleted file mode 100644
index 370145d..0000000
--- a/nfc-extras/com.android.nfc_extras.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<permissions>
- <library name="com.android.nfc_extras"
- file="/system/framework/com.android.nfc_extras.jar" />
-</permissions>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index e49546f..b4b5b58 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,6 +15,7 @@
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
+import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.os.BatteryManager;
import android.os.SystemProperties;
@@ -372,4 +373,15 @@
isDefaultOn ? 1 : 0)
!= 0;
}
+
+ /**
+ * get that {@link AudioManager#getMode()} is in ringing/call/communication(VoIP) status.
+ */
+ public static boolean isAudioModeOngoingCall(Context context) {
+ final AudioManager audioManager = context.getSystemService(AudioManager.class);
+ final int audioMode = audioManager.getMode();
+ return audioMode == AudioManager.MODE_RINGTONE
+ || audioMode == AudioManager.MODE_IN_CALL
+ || audioMode == AudioManager.MODE_IN_COMMUNICATION;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index fb268ab..e7d7ab3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -490,6 +490,7 @@
}
private void dispatchAudioModeChanged() {
+ mDeviceManager.dispatchAudioModeChanged();
synchronized (mCallbacks) {
for (BluetoothCallback callback : mCallbacks) {
callback.onAudioModeChanged();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 7d0587a..95e443c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -23,6 +23,7 @@
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.SharedPreferences;
+import android.media.AudioManager;
import android.os.ParcelUuid;
import android.os.SystemClock;
import android.text.TextUtils;
@@ -51,6 +52,7 @@
private final Context mContext;
private final LocalBluetoothAdapter mLocalAdapter;
private final LocalBluetoothProfileManager mProfileManager;
+ private final AudioManager mAudioManager;
private final BluetoothDevice mDevice;
//TODO: consider remove, BluetoothDevice.getName() is already cached
private String mName;
@@ -123,7 +125,6 @@
private boolean mIsActiveDeviceA2dp = false;
private boolean mIsActiveDeviceHeadset = false;
private boolean mIsActiveDeviceHearingAid = false;
-
/**
* Describes the current device and profile for logging.
*
@@ -185,6 +186,7 @@
mContext = context;
mLocalAdapter = adapter;
mProfileManager = profileManager;
+ mAudioManager = context.getSystemService(AudioManager.class);
mDevice = device;
mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
fillData();
@@ -538,6 +540,12 @@
}
/**
+ * Update the profile audio state.
+ */
+ void onAudioModeChanged() {
+ dispatchAttributesChanged();
+ }
+ /**
* Get the device status as active or non-active per Bluetooth profile.
*
* @param bluetoothProfile the Bluetooth profile
@@ -972,12 +980,14 @@
/**
* @return resource for string that discribes the connection state of this device.
+ * case 1: idle or playing media, show "Active" on the only one A2DP active device.
+ * case 2: in phone call, show "Active" on the only one HFP active device
*/
public String getConnectionSummary() {
- boolean profileConnected = false; // at least one profile is connected
- boolean a2dpNotConnected = false; // A2DP is preferred but not connected
- boolean hfpNotConnected = false; // HFP is preferred but not connected
- boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
+ boolean profileConnected = false; // Updated as long as BluetoothProfile is connected
+ boolean a2dpConnected = true; // A2DP is connected
+ boolean hfpConnected = true; // HFP is connected
+ boolean hearingAidConnected = true; // Hearing Aid is connected
for (LocalBluetoothProfile profile : getProfiles()) {
int connectionStatus = getProfileConnectionState(profile);
@@ -994,13 +1004,13 @@
case BluetoothProfile.STATE_DISCONNECTED:
if (profile.isProfileReady()) {
if ((profile instanceof A2dpProfile) ||
- (profile instanceof A2dpSinkProfile)){
- a2dpNotConnected = true;
+ (profile instanceof A2dpSinkProfile)) {
+ a2dpConnected = false;
} else if ((profile instanceof HeadsetProfile) ||
- (profile instanceof HfpClientProfile)) {
- hfpNotConnected = true;
- } else if (profile instanceof HearingAidProfile) {
- hearingAidNotConnected = true;
+ (profile instanceof HfpClientProfile)) {
+ hfpConnected = false;
+ } else if (profile instanceof HearingAidProfile) {
+ hearingAidConnected = false;
}
}
break;
@@ -1019,65 +1029,50 @@
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
- // Prepare the string for the Active Device summary
- String[] activeDeviceStringsArray = mContext.getResources().getStringArray(
- R.array.bluetooth_audio_active_device_summaries);
- String activeDeviceString = activeDeviceStringsArray[0]; // Default value: not active
- if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
- activeDeviceString = activeDeviceStringsArray[1]; // Active for Media and Phone
- } else {
- if (mIsActiveDeviceA2dp) {
- activeDeviceString = activeDeviceStringsArray[2]; // Active for Media only
- }
- if (mIsActiveDeviceHeadset) {
- activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
- }
- }
- if (!hearingAidNotConnected && mIsActiveDeviceHearingAid) {
- activeDeviceString = activeDeviceStringsArray[1];
- return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
- }
-
+ int stringRes = R.string.bluetooth_pairing;
+ //when profile is connected, information would be available
if (profileConnected) {
- if (a2dpNotConnected && hfpNotConnected) {
+ if (a2dpConnected || hfpConnected || hearingAidConnected) {
+ //contain battery information
if (batteryLevelPercentageString != null) {
- return mContext.getString(
- R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- batteryLevelPercentageString, activeDeviceString);
+ //device is in phone call
+ if (com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)) {
+ if (mIsActiveDeviceHeadset) {
+ stringRes = R.string.bluetooth_active_battery_level;
+ } else {
+ stringRes = R.string.bluetooth_battery_level;
+ }
+ } else {//device is not in phone call(ex. idle or playing media)
+ //need to check if A2DP and HearingAid are exclusive
+ if (mIsActiveDeviceHearingAid || mIsActiveDeviceA2dp) {
+ stringRes = R.string.bluetooth_active_battery_level;
+ } else {
+ stringRes = R.string.bluetooth_battery_level;
+ }
+ }
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp,
- activeDeviceString);
+ //no battery information
+ if (com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)) {
+ if (mIsActiveDeviceHeadset) {
+ stringRes = R.string.bluetooth_active_no_battery_level;
+ }
+ } else {
+ if (mIsActiveDeviceHearingAid || mIsActiveDeviceA2dp) {
+ stringRes = R.string.bluetooth_active_no_battery_level;
+ }
+ }
}
-
- } else if (a2dpNotConnected) {
+ } else {//unknown profile with battery information
if (batteryLevelPercentageString != null) {
- return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
- batteryLevelPercentageString, activeDeviceString);
- } else {
- return mContext.getString(R.string.bluetooth_connected_no_a2dp,
- activeDeviceString);
- }
-
- } else if (hfpNotConnected) {
- if (batteryLevelPercentageString != null) {
- return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
- batteryLevelPercentageString, activeDeviceString);
- } else {
- return mContext.getString(R.string.bluetooth_connected_no_headset,
- activeDeviceString);
- }
- } else {
- if (batteryLevelPercentageString != null) {
- return mContext.getString(R.string.bluetooth_connected_battery_level,
- batteryLevelPercentageString, activeDeviceString);
- } else {
- return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
+ stringRes = R.string.bluetooth_battery_level;
}
}
}
- return getBondState() == BluetoothDevice.BOND_BONDING ?
- mContext.getString(R.string.bluetooth_pairing) : null;
+ return (stringRes != R.string.bluetooth_pairing
+ || getBondState() == BluetoothDevice.BOND_BONDING)
+ ? mContext.getString(stringRes, batteryLevelPercentageString)
+ : null;
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 3b1e4ce..15f6983 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -358,6 +358,12 @@
}
}
+ public synchronized void dispatchAudioModeChanged() {
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ cachedDevice.onAudioModeChanged();
+ }
+ }
+
private void log(String msg) {
if (DEBUG) {
Log.d(TAG, msg);
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
index eb9bbf5..026bbd4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
@@ -48,6 +48,7 @@
private static final boolean DEBUG = false;
private static final String TAG = "InputMethdAndSubtypeUtl";
+ private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final char INPUT_METHOD_SEPARATER = ':';
private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
private static final int NOT_A_SUBTYPE_ID = -1;
@@ -177,7 +178,7 @@
final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
final boolean systemIme = imi.isSystem();
if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
- context.getActivity()).isAlwaysCheckedIme(imi, context.getActivity()))
+ context.getActivity()).isAlwaysCheckedIme(imi))
|| isImeChecked) {
if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
// imiId has just been enabled
@@ -416,4 +417,19 @@
}
return configurationLocale;
}
+
+ public static boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi) {
+ if (imi.isAuxiliaryIme() || !imi.isSystem()) {
+ return false;
+ }
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+ if (SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())
+ && subtype.isAsciiCapable()) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 442a35f..2dec6c3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -124,7 +124,7 @@
}
mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context);
mHasPriorityInSorting = imi.isSystem()
- && mInputMethodSettingValues.isValidSystemNonAuxAsciiCapableIme(imi, context);
+ && InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(imi);
setOnPreferenceClickListener(this);
setOnPreferenceChangeListener(this);
}
@@ -197,8 +197,7 @@
}
public void updatePreferenceViews() {
- final boolean isAlwaysChecked = mInputMethodSettingValues.isAlwaysCheckedIme(
- mImi, getContext());
+ final boolean isAlwaysChecked = mInputMethodSettingValues.isAlwaysCheckedIme(mImi);
// When this preference has a switch and an input method should be always enabled,
// this preference should be disabled to prevent accidentally disabling an input method.
// This preference should also be disabled in case the admin does not allow this input
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
index ab017e2..b6786d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
@@ -22,15 +22,11 @@
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.internal.inputmethod.InputMethodUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
/**
* This class is a wrapper for {@link InputMethodManager} and
@@ -47,7 +43,6 @@
private final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
private final ContentResolver mContentResolver;
private final InputMethodManager mImm;
- private final HashSet<InputMethodInfo> mAsciiCapableEnabledImis = new HashSet<>();
public static InputMethodSettingValuesWrapper getInstance(Context context) {
if (sInstance == null) {
@@ -70,50 +65,32 @@
public void refreshAllInputMethodAndSubtypes() {
mMethodList.clear();
mMethodList.addAll(mImm.getInputMethodList());
- updateAsciiCapableEnabledImis();
- }
-
- // TODO: Add a cts to ensure at least one AsciiCapableSubtypeEnabledImis exist
- private void updateAsciiCapableEnabledImis() {
- mAsciiCapableEnabledImis.clear();
- final List<InputMethodInfo> enabledImis = getEnabledInputMethodList();
- for (final InputMethodInfo imi : enabledImis) {
- final int subtypeCount = imi.getSubtypeCount();
- for (int i = 0; i < subtypeCount; ++i) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(i);
- if (InputMethodUtils.SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())
- && subtype.isAsciiCapable()) {
- mAsciiCapableEnabledImis.add(imi);
- break;
- }
- }
- }
}
public List<InputMethodInfo> getInputMethodList() {
return new ArrayList<>(mMethodList);
}
- public boolean isAlwaysCheckedIme(InputMethodInfo imi, Context context) {
+ public boolean isAlwaysCheckedIme(InputMethodInfo imi) {
final boolean isEnabled = isEnabledImi(imi);
if (getEnabledInputMethodList().size() <= 1 && isEnabled) {
return true;
}
final int enabledValidSystemNonAuxAsciiCapableImeCount =
- getEnabledValidSystemNonAuxAsciiCapableImeCount(context);
+ getEnabledValidSystemNonAuxAsciiCapableImeCount();
return enabledValidSystemNonAuxAsciiCapableImeCount <= 1
&& !(enabledValidSystemNonAuxAsciiCapableImeCount == 1 && !isEnabled)
&& imi.isSystem()
- && isValidSystemNonAuxAsciiCapableIme(imi, context);
+ && InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(imi);
}
- private int getEnabledValidSystemNonAuxAsciiCapableImeCount(Context context) {
+ private int getEnabledValidSystemNonAuxAsciiCapableImeCount() {
int count = 0;
final List<InputMethodInfo> enabledImis = getEnabledInputMethodList();
for (final InputMethodInfo imi : enabledImis) {
- if (isValidSystemNonAuxAsciiCapableIme(imi, context)) {
+ if (InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(imi)) {
++count;
}
}
@@ -133,25 +110,6 @@
return false;
}
- public boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi, Context context) {
- if (imi.isAuxiliaryIme()) {
- return false;
- }
- final Locale systemLocale = context.getResources().getConfiguration().locale;
- if (InputMethodUtils.isSystemImeThatHasSubtypeOf(imi, context,
- true /* checkDefaultAttribute */, systemLocale, false /* checkCountry */,
- InputMethodUtils.SUBTYPE_MODE_ANY)) {
- return true;
- }
- if (mAsciiCapableEnabledImis.isEmpty()) {
- Log.w(TAG, "ascii capable subtype enabled imi not found. Fall back to English"
- + " Keyboard subtype.");
- return InputMethodUtils.containsSubtypeOf(imi, Locale.ENGLISH, false /* checkCountry */,
- InputMethodUtils.SUBTYPE_MODE_KEYBOARD);
- }
- return mAsciiCapableEnabledImis.contains(imi);
- }
-
/**
* Returns the list of the enabled {@link InputMethodInfo} determined by
* {@link android.provider.Settings.Secure#ENABLED_INPUT_METHODS} rather than just returning
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
index 83c00ea..bb43581 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
@@ -23,7 +23,6 @@
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.InputMethodUtils;
import java.text.Collator;
import java.util.Locale;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
index ab2b97f..93b038e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
@@ -125,6 +125,7 @@
final InputMethodSubtype systemLocaleSubtype =
new InputMethodSubtype.InputMethodSubtypeBuilder()
.setIsAsciiCapable(true)
+ .setSubtypeMode("keyboard")
.setSubtypeLocale(systemLocale.getLanguage())
.build();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 06e31aa..09a2bd2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -33,6 +34,7 @@
import android.content.Intent;
import android.content.res.Resources;
import android.location.LocationManager;
+import android.media.AudioManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
@@ -50,6 +52,7 @@
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowAudioManager;
import org.robolectric.shadows.ShadowSettings;
import java.util.HashMap;
@@ -67,6 +70,7 @@
private static final String PERCENTAGE_50 = "50%";
private static final String PERCENTAGE_100 = "100%";
+ private ShadowAudioManager mShadowAudioManager;
private Context mContext;
@Mock
private LocationManager mLocationManager;
@@ -77,6 +81,7 @@
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
ShadowSecure.reset();
+ mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
}
@Test
@@ -193,4 +198,32 @@
// Do nothing
}
}
+
+ @Test
+ public void isAudioModeOngoingCall_modeInCommunication_returnTrue() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+
+ assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
+ }
+
+ @Test
+ public void isAudioModeOngoingCall_modeInCall_returnTrue() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+ assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
+ }
+
+ @Test
+ public void isAudioModeOngoingCall_modeRingtone_returnTrue() {
+ mShadowAudioManager.setMode(AudioManager.MODE_RINGTONE);
+
+ assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
+ }
+
+ @Test
+ public void isAudioModeOngoingCall_modeNormal_returnFalse() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+
+ assertThat(Utils.isAudioModeOngoingCall(mContext)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 7863fc5..5853dca 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -23,23 +23,25 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.media.AudioManager;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowAudioManager;
-@RunWith(RobolectricTestRunner.class)
-@Config(resourceDir = "../../res")
+@RunWith(SettingsLibRobolectricTestRunner.class)
public class CachedBluetoothDeviceTest {
private final static String DEVICE_NAME = "TestName";
private final static String DEVICE_ALIAS = "TestAlias";
@@ -60,6 +62,7 @@
@Mock
private BluetoothDevice mDevice;
private CachedBluetoothDevice mCachedDevice;
+ private ShadowAudioManager mShadowAudioManager;
private Context mContext;
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
@@ -67,6 +70,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+ mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
when(mHfpProfile.isProfileReady()).thenReturn(true);
@@ -83,7 +87,7 @@
// Test without battery level
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -93,7 +97,7 @@
mBatteryLevel = 10;
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -104,7 +108,7 @@
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -119,23 +123,23 @@
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
// Disconnect HFP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected (no phone), battery 10%");
+ "10% battery");
// Disconnect A2DP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected (no media), battery 10%");
+ "10% battery");
// Disconnect both HFP and A2DP and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected (no phone or media), battery 10%");
+ "10% battery");
// Disconnect all profiles and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -147,16 +151,16 @@
// Test without battery level
// Set A2DP profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for A2DP and test connection state summary
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Test with battery level
mBatteryLevel = 10;
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected, battery 10%, active(media)");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Active, 10% battery");
// Set A2DP profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -167,7 +171,7 @@
// Set A2DP profile to be connected, Active and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Set A2DP profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -179,16 +183,18 @@
// Test without battery level
// Set HFP profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for HFP and test connection state summary
+ mCachedDevice.onAudioModeChanged();
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Test with battery level
mBatteryLevel = 10;
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected, battery 10%, active(phone)");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Active, 10% battery");
// Set HFP profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -199,7 +205,7 @@
// Set HFP profile to be connected, Active and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Set HFP profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -211,15 +217,16 @@
// Test without battery level
// Set Hearing Aid profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for Hearing Aid and test connection state summary
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Set Hearing Aid profile to be disconnected and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
- mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.
+ onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@@ -229,23 +236,23 @@
// Set A2DP and HFP profiles to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for A2DP and HFP and test connection state summary
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Test with battery level
mBatteryLevel = 10;
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected, battery 10%, active");
+ "Active, 10% battery");
// Disconnect A2DP only and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected (no media), battery 10%, active(phone)");
+ "10% battery");
// Disconnect HFP only and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
@@ -253,7 +260,7 @@
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Connected (no phone), battery 10%, active(media)");
+ "Active, 10% battery");
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
@@ -262,7 +269,7 @@
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Set A2DP and HFP profiles to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 0eec247..d0a0686 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -22,6 +22,14 @@
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -187,4 +195,75 @@
+ "|ime3;subtype2;subtype3:ime0;subtype1;subtype0"
+ "|ime4;subtype3;subtype2:ime0;subtype1;subtype0");
}
+
+ @Test
+ public void isValidSystemNonAuxAsciiCapableIme() {
+ // System IME w/ no subtype
+ assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false)))
+ .isFalse();
+
+ // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false, createDummySubtype("keyboard", false, false))))
+ .isFalse();
+
+ // System IME w/ non-Aux and ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false, createDummySubtype("keyboard", false, true))))
+ .isTrue();
+
+ // System IME w/ Aux and ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, true, createDummySubtype("keyboard", true, true))))
+ .isFalse();
+
+ // System IME w/ non-Aux and ASCII-capable "voice" subtype
+ assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false, createDummySubtype("voice", false, true))))
+ .isFalse();
+
+ // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
+ assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false,
+ createDummySubtype("keyboard", false, true),
+ createDummySubtype("keyboard", false, false))))
+ .isTrue();
+
+ // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
+ .isFalse();
+ }
+
+ private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
+ InputMethodSubtype... subtypes) {
+ final ResolveInfo ri = new ResolveInfo();
+ final ServiceInfo si = new ServiceInfo();
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = "com.example.android.dummyime";
+ ai.enabled = true;
+ ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
+ si.applicationInfo = ai;
+ si.enabled = true;
+ si.packageName = "com.example.android.dummyime";
+ si.name = "Dummy IME";
+ si.exported = true;
+ si.nonLocalizedLabel = "Dummy IME";
+ ri.serviceInfo = si;
+ return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
+ }
+
+ private static InputMethodSubtype createDummySubtype(
+ String mode, boolean isAuxiliary, boolean isAsciiCapable) {
+ return new InputMethodSubtypeBuilder()
+ .setSubtypeNameResId(0)
+ .setSubtypeIconResId(0)
+ .setSubtypeLocale("en_US")
+ .setLanguageTag("en-US")
+ .setSubtypeMode(mode)
+ .setIsAuxiliary(isAuxiliary)
+ .setIsAsciiCapable(isAsciiCapable)
+ .build();
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1d3e521..32aafea 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2935,7 +2935,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 164;
+ private static final int SETTINGS_VERSION = 165;
private final int mUserId;
@@ -3710,17 +3710,7 @@
}
if (currentVersion == 162) {
- // Version 162: Add a gesture for silencing phones
- final SettingsState settings = getGlobalSettingsLocked();
- final Setting currentSetting = settings.getSettingLocked(
- Global.SHOW_ZEN_UPGRADE_NOTIFICATION);
- if (!currentSetting.isNull()
- && TextUtils.equals("0", currentSetting.getValue())) {
- settings.insertSettingLocked(
- Global.SHOW_ZEN_UPGRADE_NOTIFICATION, "1",
- null, true, SettingsState.SYSTEM_PACKAGE_NAME);
- }
-
+ // Version 162: REMOVED: Add a gesture for silencing phones
currentVersion = 163;
}
@@ -3742,6 +3732,21 @@
currentVersion = 164;
}
+ if (currentVersion == 164) {
+ // Version 164: Add a gesture for silencing phones
+ final SettingsState settings = getGlobalSettingsLocked();
+ final Setting currentSetting = settings.getSettingLocked(
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION);
+ if (!currentSetting.isNull()
+ && TextUtils.equals("0", currentSetting.getValue())) {
+ settings.insertSettingLocked(
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION, "1",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ currentVersion = 165;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 6dea493..3193101 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -46,8 +46,7 @@
style="@style/widget_big_thin"
android:format12Hour="@string/keyguard_widget_12_hours_format"
android:format24Hour="@string/keyguard_widget_24_hours_format"
- android:baselineAligned="true"
- android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+ android:baselineAligned="true" />
<include layout="@layout/keyguard_status_area" />
<ImageView
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 611aa43..db03ed2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -28,12 +28,14 @@
android:clipToPadding="false"
android:orientation="vertical"
android:layout_centerHorizontal="true">
- <TextView android:id="@+id/title"
+ <com.android.systemui.statusbar.AlphaOptimizedTextView
+ android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="7dp"
+ android:layout_marginBottom="@dimen/widget_title_bottom_margin"
android:paddingStart="64dp"
android:paddingEnd="64dp"
+ android:visibility="gone"
android:textColor="?attr/wallpaperTextColor"
android:theme="@style/TextAppearance.Keyguard"
/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index fa14d1b..ba05ccf 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -67,21 +67,17 @@
android:singleLine="true"
style="@style/widget_big_thin"
android:format12Hour="@string/keyguard_widget_12_hours_format"
- android:format24Hour="@string/keyguard_widget_24_hours_format"
- android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+ android:format24Hour="@string/keyguard_widget_24_hours_format" />
<View
android:id="@+id/clock_separator"
android:layout_width="@dimen/widget_separator_width"
android:layout_height="@dimen/widget_separator_thickness"
- android:layout_marginTop="22dp"
android:layout_below="@id/clock_view"
android:background="#f00"
- android:backgroundTint="?attr/wallpaperTextColor"
android:layout_centerHorizontal="true" />
<include layout="@layout/keyguard_status_area"
android:id="@+id/keyguard_status_area"
- android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/clock_separator" />
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index 4ae7cb9..fdca44d 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -28,7 +28,6 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">100dp</dimen>
<dimen name="widget_label_font_size">16sp</dimen>
- <dimen name="bottom_text_spacing_digital">-1dp</dimen>
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 37de433..712b6e5 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -41,14 +41,14 @@
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">-10dip</dimen>
- <!-- Default clock parameters -->
- <dimen name="bottom_text_spacing_digital">-24dp</dimen>
<!-- Slice header -->
- <dimen name="widget_title_font_size">24sp</dimen>
- <!-- Slice subtitle -->
- <dimen name="widget_label_font_size">16sp</dimen>
+ <dimen name="widget_title_font_size">24dp</dimen>
+ <dimen name="widget_title_bottom_margin">7dp</dimen>
+ <dimen name="bottom_text_spacing_digital">0dp</dimen>
+ <!-- Slice subtitle -->
+ <dimen name="widget_label_font_size">16dp</dimen>
<!-- Slice offset when pulsing -->
- <dimen name="widget_pulsing_bottom_padding">24dp</dimen>
+ <dimen name="widget_pulsing_bottom_padding">48dp</dimen>
<!-- Clock without header -->
<dimen name="widget_big_font_size">64dp</dimen>
<!-- Clock with header -->
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 5f52e2a..b90b4dd 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -53,6 +53,7 @@
</style>
<style name="widget_big_thin">
<item name="android:textSize">@dimen/widget_big_font_size</item>
+ <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
<item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
</style>
@@ -89,6 +90,7 @@
<style name="TextAppearance.Keyguard.Secondary">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
+ <item name="android:lines">1</item>
<item name="android:textSize">@dimen/widget_label_font_size</item>
</style>
diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
index c464ba6..93adaa0 100644
--- a/packages/SystemUI/res/drawable/smart_reply_button_background.xml
+++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
@@ -21,9 +21,9 @@
<item>
<inset
android:insetLeft="0dp"
- android:insetTop="7dp"
+ android:insetTop="8dp"
android:insetRight="0dp"
- android:insetBottom="5dp">
+ android:insetBottom="8dp">
<shape android:shape="rectangle">
<corners android:radius="8dp" />
<stroke android:width="1dp" android:color="@color/smart_reply_button_stroke" />
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
index 98e6e82..a490c4b 100644
--- a/packages/SystemUI/res/layout/smart_reply_button.xml
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -26,7 +26,7 @@
android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
android:background="@drawable/smart_reply_button_background"
android:gravity="center"
- android:fontFamily="sans-serif-medium"
+ android:fontFamily="roboto-medium"
android:textSize="@dimen/smart_reply_button_font_size"
android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
android:textColor="@color/smart_reply_button_text"
diff --git a/packages/SystemUI/res/layout/status_bar_dnd_suppressing_notifications.xml b/packages/SystemUI/res/layout/status_bar_dnd_suppressing_notifications.xml
new file mode 100644
index 0000000..eff9b36
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_dnd_suppressing_notifications.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Extends Framelayout -->
+<com.android.systemui.statusbar.DndSuppressingNotificationsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/hidden_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/hidden_notifications"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="64dp"
+ android:paddingTop="28dp"
+ android:gravity="top|center_horizontal"
+ android:textColor="?attr/wallpaperTextColor"
+ android:textSize="16sp"
+ android:text="@string/dnd_suppressing_shade_text"/>
+</com.android.systemui.statusbar.DndSuppressingNotificationsView>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ac9fb2b..eb71911 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -140,6 +140,9 @@
notification panel collapses -->
<dimen name="shelf_appear_translation">42dp</dimen>
+ <!-- Vertical translation of pulsing notification animations -->
+ <dimen name="pulsing_notification_appear_translation">10dp</dimen>
+
<!-- The amount the content shifts upwards when transforming into the icon -->
<dimen name="notification_icon_transform_content_shift">32dp</dimen>
@@ -927,12 +930,13 @@
<!-- Home button padding for sizing -->
<dimen name="home_padding">16dp</dimen>
- <!-- Smart reply button -->
+ <!-- Smart reply button. Total height 48dp, visible height 32dp. -->
<dimen name="smart_reply_button_spacing">8dp</dimen>
- <dimen name="smart_reply_button_padding_vertical">10dp</dimen>
- <dimen name="smart_reply_button_padding_horizontal_single_line">16dp</dimen>
- <dimen name="smart_reply_button_padding_horizontal_double_line">16dp</dimen>
- <dimen name="smart_reply_button_min_height">32dp</dimen>
+ <dimen name="smart_reply_button_padding_vertical">14dp</dimen>
+ <!-- Note: The following two paddings need to be different until b/78876518 is fixed. -->
+ <dimen name="smart_reply_button_padding_horizontal_single_line">20dp</dimen>
+ <dimen name="smart_reply_button_padding_horizontal_double_line">19dp</dimen>
+ <dimen name="smart_reply_button_min_height">48dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
<dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 697ab06..50e7b5c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -240,6 +240,8 @@
<string name="accessibility_waiting_for_fingerprint">Waiting for fingerprint</string>
<!-- Accessibility action of the unlock button when fingerpint is on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_unlock_without_fingerprint">Unlock without using your fingerprint</string>
+ <!-- Content description of the Trusted Face icon for accessibility. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_scanning_face">Scanning face</string>
<!-- Click action label for accessibility for the smart reply buttons (not shown on-screen).". [CHAR LIMIT=NONE] -->
<string name="accessibility_send_smart_reply">Send</string>
<!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] -->
@@ -1062,7 +1064,7 @@
<string name="manage_notifications_text">Manage notifications</string>
<!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
- <string name="dnd_suppressing_shade_text">Do Not Disturb is hiding notifications</string>
+ <string name="dnd_suppressing_shade_text">Notifications hidden by Do Not Disturb</string>
<!-- Media projection permission dialog action text. [CHAR LIMIT=60] -->
<string name="media_projection_action_text">Start now</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java
new file mode 100644
index 0000000..e93e78d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import com.android.internal.logging.MetricsLogger;
+
+public class MetricsLoggerCompat {
+
+ private final MetricsLogger mMetricsLogger;
+
+ public MetricsLoggerCompat() {
+ mMetricsLogger = new MetricsLogger();
+ }
+
+ public void action(int category) {
+ mMetricsLogger.action(category);
+ }
+
+ public void action(int category, int value) {
+ mMetricsLogger.action(category, value);
+ }
+
+ public void visible(int category) {
+ mMetricsLogger.visible(category);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 07b980e..ed2f831 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -26,6 +26,10 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
@@ -58,6 +62,11 @@
public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+ public static final int NAV_BAR_POS_INVALID = -1;
+ public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
+ public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
+ public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
+
public static final int ACTIVITY_TYPE_STANDARD = WindowConfiguration.ACTIVITY_TYPE_STANDARD;
public static final int WINDOWING_MODE_UNDEFINED = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -141,4 +150,20 @@
Log.w(TAG, "Failed to set recents visibility");
}
}
+
+ /**
+ * @return The side of the screen where navigation bar is positioned.
+ * @see #NAV_BAR_POS_RIGHT
+ * @see #NAV_BAR_POS_LEFT
+ * @see #NAV_BAR_POS_BOTTOM
+ * @see #NAV_BAR_POS_INVALID
+ */
+ public int getNavBarPosition() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get nav bar position");
+ }
+ return NAV_BAR_POS_INVALID;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index e936c3b..5ce3339 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -34,6 +34,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.Button;
import android.widget.LinearLayout;
@@ -46,8 +47,9 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.wakelock.WakeLock;
+import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
import java.util.HashMap;
import java.util.List;
@@ -64,21 +66,29 @@
* View visible under the clock on the lock screen and AoD.
*/
public class KeyguardSliceView extends LinearLayout implements View.OnClickListener,
- Observer<Slice>, TunerService.Tunable {
+ Observer<Slice>, TunerService.Tunable, ConfigurationController.ConfigurationListener {
private static final String TAG = "KeyguardSliceView";
+ public static final int DEFAULT_ANIM_DURATION = 550;
+
private final HashMap<View, PendingIntent> mClickActions;
private Uri mKeyguardSliceUri;
- private TextView mTitle;
- private LinearLayout mRow;
+ @VisibleForTesting
+ TextView mTitle;
+ private Row mRow;
private int mTextColor;
private float mDarkAmount = 0;
private LiveData<Slice> mLiveData;
private int mIconSize;
- private Consumer<Boolean> mListener;
+ /**
+ * Listener called whenever the view contents change.
+ * Boolean will be true when the change happens animated.
+ */
+ private Consumer<Boolean> mContentChangeListener;
private boolean mHasHeader;
- private boolean mHideContent;
+ private Slice mSlice;
+ private boolean mPulsing;
public KeyguardSliceView(Context context) {
this(context, null, 0);
@@ -95,6 +105,18 @@
tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
mClickActions = new HashMap<>();
+
+ LayoutTransition transition = new LayoutTransition();
+ transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
+ transition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
+ transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2);
+ transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+ transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
+ transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
+ transition.setAnimateParentHierarchy(false);
+ transition.addTransitionListener(new SliceViewTransitionListener());
+ setLayoutTransition(transition);
}
@Override
@@ -112,6 +134,7 @@
// Make sure we always have the most current slice
mLiveData.observeForever(this);
+ Dependency.get(ConfigurationController.class).addCallback(this);
}
@Override
@@ -119,17 +142,29 @@
super.onDetachedFromWindow();
mLiveData.removeObserver(this);
+ Dependency.get(ConfigurationController.class).removeCallback(this);
}
- private void showSlice(Slice slice) {
+ private void showSlice() {
+ if (mPulsing) {
+ mTitle.setVisibility(GONE);
+ mRow.setVisibility(GONE);
+ mContentChangeListener.accept(getLayoutTransition() != null);
+ return;
+ }
- ListContent lc = new ListContent(getContext(), slice);
+ if (mSlice == null) {
+ return;
+ }
+
+ ListContent lc = new ListContent(getContext(), mSlice);
mHasHeader = lc.hasHeader();
List<SliceItem> subItems = lc.getRowItems();
if (!mHasHeader) {
mTitle.setVisibility(GONE);
} else {
mTitle.setVisibility(VISIBLE);
+
// If there's a header it'll be the first subitem
RowContent header = new RowContent(getContext(), subItems.get(0),
true /* showStartItem */);
@@ -154,6 +189,7 @@
final int subItemsCount = subItems.size();
final int blendedColor = getTextColor();
final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
+ mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
for (int i = startIndex; i < subItemsCount; i++) {
SliceItem item = subItems.get(i);
RowContent rc = new RowContent(getContext(), item, true /* showStartItem */);
@@ -200,15 +236,20 @@
}
}
- updateVisibility();
- mListener.accept(mHasHeader);
+ if (mContentChangeListener != null) {
+ mContentChangeListener.accept(getLayoutTransition() != null);
+ }
}
- private void updateVisibility() {
- final boolean hasContent = mHasHeader || mRow.getChildCount() > 0;
- final int visibility = hasContent && !mHideContent ? VISIBLE : GONE;
- if (visibility != getVisibility()) {
- setVisibility(visibility);
+ public void setPulsing(boolean pulsing, boolean animate) {
+ mPulsing = pulsing;
+ LayoutTransition transition = getLayoutTransition();
+ if (!animate) {
+ setLayoutTransition(null);
+ }
+ showSlice();
+ if (!animate) {
+ setLayoutTransition(transition);
}
}
@@ -252,8 +293,9 @@
return optimalString.toString();
}
- public void setDark(float darkAmount) {
+ public void setDarkAmount(float darkAmount) {
mDarkAmount = darkAmount;
+ mRow.setDarkAmount(darkAmount);
updateTextColors();
}
@@ -281,12 +323,17 @@
}
}
- public void setListener(Consumer<Boolean> listener) {
- mListener = listener;
+ /**
+ * Listener that gets invoked every time the title or the row visibility changes.
+ * Parameter will be {@code true} whenever the change happens animated.
+ * @param contentChangeListener The listener.
+ */
+ public void setContentChangeListener(Consumer<Boolean> contentChangeListener) {
+ mContentChangeListener = contentChangeListener;
}
public boolean hasHeader() {
- return mHasHeader;
+ return mTitle.getVisibility() == VISIBLE;
}
/**
@@ -295,7 +342,8 @@
*/
@Override
public void onChanged(Slice slice) {
- showSlice(slice);
+ mSlice = slice;
+ showSlice();
}
@Override
@@ -333,15 +381,20 @@
updateTextColors();
}
- public void setHideContent(boolean hideContent) {
- mHideContent = hideContent;
- updateVisibility();
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size);
}
public static class Row extends LinearLayout {
- private static final long ROW_ANIM_DURATION = 350;
- private final WakeLock mAnimationWakeLock;
+ /**
+ * This view is visible in AOD, which means that the device will sleep if we
+ * don't hold a wake lock. We want to enter doze only after all views have reached
+ * their desired positions.
+ */
+ private final Animation.AnimationListener mKeepAwakeListener;
+ private float mDarkAmount;
public Row(Context context) {
this(context, null);
@@ -357,16 +410,13 @@
public Row(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mAnimationWakeLock = WakeLock.createPartial(context, "slice animation");
+ mKeepAwakeListener = new KeepAwakeAnimationListener(mContext);
}
@Override
protected void onFinishInflate() {
LayoutTransition transition = new LayoutTransition();
- transition.setDuration(ROW_ANIM_DURATION);
- transition.setStagger(LayoutTransition.CHANGING, ROW_ANIM_DURATION);
- transition.setStagger(LayoutTransition.CHANGE_APPEARING, ROW_ANIM_DURATION);
- transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, ROW_ANIM_DURATION);
+ transition.setDuration(DEFAULT_ANIM_DURATION);
PropertyValuesHolder left = PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder right = PropertyValuesHolder.ofInt("right", 0, 1);
@@ -378,7 +428,8 @@
Interpolators.ACCELERATE_DECELERATE);
transition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
Interpolators.ACCELERATE_DECELERATE);
- transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, ROW_ANIM_DURATION);
+ transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION);
+ transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, DEFAULT_ANIM_DURATION);
ObjectAnimator appearAnimator = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
transition.setAnimator(LayoutTransition.APPEARING, appearAnimator);
@@ -386,31 +437,11 @@
ObjectAnimator disappearAnimator = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
- transition.setDuration(LayoutTransition.DISAPPEARING, ROW_ANIM_DURATION / 2);
+ transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4);
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator);
transition.setAnimateParentHierarchy(false);
setLayoutTransition(transition);
-
- // This view is visible in AOD, which means that the device will sleep if we
- // don't hold a wake lock. We want to enter doze only after all views have reached
- // their desired positions.
- setLayoutAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- mAnimationWakeLock.acquire();
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- mAnimationWakeLock.release();
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
-
- }
- });
}
@Override
@@ -424,23 +455,58 @@
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+
+ public void setDarkAmount(float darkAmount) {
+ boolean isAwake = darkAmount != 0;
+ boolean wasAwake = mDarkAmount != 0;
+ if (isAwake == wasAwake) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ setLayoutAnimationListener(isAwake ? null : mKeepAwakeListener);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
/**
* Representation of an item that appears under the clock on main keyguard message.
*/
- private class KeyguardSliceButton extends Button {
+ @VisibleForTesting
+ static class KeyguardSliceButton extends Button implements
+ ConfigurationController.ConfigurationListener {
+ private final Context mContext;
public KeyguardSliceButton(Context context) {
super(context, null /* attrs */, 0 /* styleAttr */,
com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary);
- int horizontalPadding = (int) context.getResources()
+ mContext = context;
+ onDensityOrFontScaleChanged();
+ setEllipsize(TruncateAt.END);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ Dependency.get(ConfigurationController.class).addCallback(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ Dependency.get(ConfigurationController.class).removeCallback(this);
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ int horizontalPadding = (int) mContext.getResources()
.getDimension(R.dimen.widget_horizontal_padding);
setPadding(horizontalPadding / 2, 0, horizontalPadding / 2, 0);
- setCompoundDrawablePadding((int) context.getResources()
+ setCompoundDrawablePadding((int) mContext.getResources()
.getDimension(R.dimen.widget_icon_padding));
- setMaxLines(1);
- setEllipsize(TruncateAt.END);
}
@Override
@@ -465,4 +531,38 @@
}
}
}
+
+ private class SliceViewTransitionListener implements LayoutTransition.TransitionListener {
+ @Override
+ public void startTransition(LayoutTransition transition, ViewGroup container, View view,
+ int transitionType) {
+ switch (transitionType) {
+ case LayoutTransition.APPEARING:
+ int translation = getResources().getDimensionPixelSize(
+ R.dimen.pulsing_notification_appear_translation);
+ view.setTranslationY(translation);
+ view.animate()
+ .translationY(0)
+ .setDuration(DEFAULT_ANIM_DURATION)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .start();
+ break;
+ case LayoutTransition.DISAPPEARING:
+ if (view == mTitle) {
+ // Translate the view to the inverse of its height, so the layout event
+ // won't misposition it.
+ LayoutParams params = (LayoutParams) mTitle.getLayoutParams();
+ int margin = params.topMargin + params.bottomMargin;
+ mTitle.setTranslationY(-mTitle.getHeight() - margin);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void endTransition(LayoutTransition transition, ViewGroup container, View view,
+ int transitionType) {
+
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index a4a3d14..ff182de3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,12 +16,11 @@
package com.android.keyguard;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
-import android.app.AlarmManager;
import android.app.IActivityManager;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
@@ -37,18 +36,24 @@
import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.GridLayout;
+import android.widget.RelativeLayout;
import android.widget.TextClock;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
import com.google.android.collect.Sets;
import java.util.Locale;
-public class KeyguardStatusView extends GridLayout {
+public class KeyguardStatusView extends GridLayout implements
+ ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardStatusView";
private static final int MARQUEE_DELAY_MS = 2000;
@@ -56,13 +61,11 @@
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
private final float mSmallClockScale;
- private final float mWidgetPadding;
private TextView mLogoutView;
private TextClock mClockView;
private View mClockSeparator;
private TextView mOwnerInfo;
- private ViewGroup mClockContainer;
private KeyguardSliceView mKeyguardSlice;
private Runnable mPendingMarqueeStart;
private Handler mHandler;
@@ -71,19 +74,22 @@
private boolean mPulsing;
private float mDarkAmount = 0;
private int mTextColor;
+ private float mWidgetPadding;
+ private boolean mAnimateLayout;
+ private int mLastLayoutHeight;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onTimeChanged() {
- refresh();
+ refreshTime();
}
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (showing) {
if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
- refresh();
+ refreshTime();
updateOwnerInfo();
updateLogoutView();
}
@@ -101,7 +107,7 @@
@Override
public void onUserSwitchComplete(int userId) {
- refresh();
+ refreshFormat();
updateOwnerInfo();
updateLogoutView();
}
@@ -127,7 +133,7 @@
mHandler = new Handler(Looper.myLooper());
mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size)
/ getResources().getDimension(R.dimen.widget_big_font_size);
- mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
+ onDensityOrFontScaleChanged();
}
private void setEnableMarquee(boolean enabled) {
@@ -160,7 +166,6 @@
mLogoutView = findViewById(R.id.logout);
mLogoutView.setOnClickListener(this::onLogoutClicked);
- mClockContainer = findViewById(R.id.keyguard_clock_container);
mClockView = findViewById(R.id.clock_view);
mClockView.setShowCurrentUserTime(true);
if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
@@ -169,46 +174,111 @@
mOwnerInfo = findViewById(R.id.owner_info);
mKeyguardSlice = findViewById(R.id.keyguard_status_area);
mClockSeparator = findViewById(R.id.clock_separator);
- mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice, mClockSeparator);
+ mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice);
mTextColor = mClockView.getCurrentTextColor();
- mKeyguardSlice.setListener(this::onSliceContentChanged);
- onSliceContentChanged(mKeyguardSlice.hasHeader());
+ mClockView.addOnLayoutChangeListener(this);
+ mClockSeparator.addOnLayoutChangeListener(this);
+ mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
+ onSliceContentChanged(false /* animated */);
boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
setEnableMarquee(shouldMarquee);
- refresh();
+ refreshFormat();
updateOwnerInfo();
updateLogoutView();
+ updateDark();
// Disable elegant text height because our fancy colon makes the ymin value huge for no
// reason.
mClockView.setElegantTextHeight(false);
}
- private void onSliceContentChanged(boolean hasHeader) {
- final boolean smallClock = hasHeader || mPulsing;
- final float clockScale = smallClock ? mSmallClockScale : 1;
- float translation = (mClockView.getHeight() - (mClockView.getHeight() * clockScale)) / 2f;
- if (smallClock) {
- translation -= mWidgetPadding;
+ private void onSliceContentChanged(boolean animated) {
+ mAnimateLayout = animated;
+ boolean smallClock = mKeyguardSlice.hasHeader() || mPulsing;
+ float clockScale = smallClock ? mSmallClockScale : 1;
+
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
+ int height = mClockView.getHeight();
+ layoutParams.bottomMargin = (int) -(height - (clockScale * height));
+ mClockView.setLayoutParams(layoutParams);
+
+ layoutParams = (RelativeLayout.LayoutParams) mClockSeparator.getLayoutParams();
+ layoutParams.topMargin = smallClock ? (int) mWidgetPadding : 0;
+ layoutParams.bottomMargin = layoutParams.topMargin;
+ mClockSeparator.setLayoutParams(layoutParams);
+ }
+
+ /**
+ * Animate clock and its separator when necessary.
+ */
+ @Override
+ public void onLayoutChange(View view, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ int heightOffset = mPulsing ? 0 : getHeight() - mLastLayoutHeight;
+ boolean hasHeader = mKeyguardSlice.hasHeader();
+ boolean smallClock = hasHeader || mPulsing;
+ long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
+ long delay = smallClock ? 0 : duration / 4;
+
+ if (view == mClockView) {
+ float clockScale = smallClock ? mSmallClockScale : 1;
+ if (mAnimateLayout) {
+ mClockView.setY(oldTop + heightOffset);
+ mClockView.animate().cancel();
+ mClockView.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(duration)
+ .setListener(new ClipChildrenAnimationListener())
+ .setStartDelay(delay)
+ .y(top)
+ .scaleX(clockScale)
+ .scaleY(clockScale)
+ .start();
+ } else {
+ mClockView.setY(top);
+ mClockView.setScaleX(clockScale);
+ mClockView.setScaleY(clockScale);
+ }
+ } else if (view == mClockSeparator) {
+ boolean hasSeparator = hasHeader && !mPulsing;
+ float alpha = hasSeparator ? 1 : 0;
+ if (mAnimateLayout) {
+ boolean isAwake = mDarkAmount != 0;
+ mClockSeparator.setY(oldTop + heightOffset);
+ mClockSeparator.animate().cancel();
+ mClockSeparator.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(duration)
+ .setListener(isAwake ? null : new KeepAwakeAnimationListener(getContext()))
+ .setStartDelay(delay)
+ .y(top)
+ .alpha(alpha)
+ .start();
+ } else {
+ mClockSeparator.setY(top);
+ mClockSeparator.setAlpha(alpha);
+ }
}
- mClockView.setTranslationY(translation);
- mClockView.setScaleX(clockScale);
- mClockView.setScaleY(clockScale);
- mClockSeparator.setVisibility(hasHeader && !mPulsing ? VISIBLE : GONE);
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
- // Some layouts like burmese have a different margin for the clock
- MarginLayoutParams layoutParams = (MarginLayoutParams) mClockView.getLayoutParams();
- layoutParams.bottomMargin = getResources().getDimensionPixelSize(
- R.dimen.bottom_text_spacing_digital);
- mClockView.setLayoutParams(layoutParams);
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mClockView.setPivotX(mClockView.getWidth() / 2);
+ mClockView.setPivotY(0);
+ mLastLayoutHeight = getHeight();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
+ if (mClockView != null) {
+ mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
+ }
if (mOwnerInfo != null) {
mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
@@ -219,17 +289,10 @@
mClockView.refresh();
}
- private void refresh() {
+ private void refreshFormat() {
Patterns.update(mContext);
- refreshTime();
- }
-
- public int getClockBottom() {
- if (mOwnerInfo != null && mOwnerInfo.getVisibility() == VISIBLE) {
- return mOwnerInfo.getBottom();
- } else {
- return mClockContainer.getBottom();
- }
+ mClockView.setFormat12Hour(Patterns.clockView12);
+ mClockView.setFormat24Hour(Patterns.clockView24);
}
public int getLogoutButtonHeight() {
@@ -249,34 +312,8 @@
private void updateOwnerInfo() {
if (mOwnerInfo == null) return;
- String ownerInfo = getOwnerInfo();
- if (!TextUtils.isEmpty(ownerInfo)) {
- mOwnerInfo.setVisibility(View.VISIBLE);
- mOwnerInfo.setText(ownerInfo);
- } else {
- mOwnerInfo.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
- }
-
- private String getOwnerInfo() {
- String info = null;
- if (mLockPatternUtils.isDeviceOwnerInfoEnabled()) {
- // Use the device owner information set by device policy client via
- // device policy manager.
- info = mLockPatternUtils.getDeviceOwnerInfo();
- } else {
+ String info = mLockPatternUtils.getDeviceOwnerInfo();
+ if (info == null) {
// Use the current user owner information if enabled.
final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
KeyguardUpdateMonitor.getCurrentUser());
@@ -284,7 +321,26 @@
info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
}
}
- return info;
+ mOwnerInfo.setText(info);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
+ Dependency.get(ConfigurationController.class).addCallback(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
+ Dependency.get(ConfigurationController.class).removeCallback(this);
+ }
+
+ @Override
+ public void onLocaleListChanged() {
+ refreshFormat();
}
@Override
@@ -329,32 +385,27 @@
return;
}
mDarkAmount = darkAmount;
-
- boolean dark = darkAmount == 1;
- mLogoutView.setAlpha(dark ? 0 : 1);
- final int N = mClockContainer.getChildCount();
- for (int i = 0; i < N; i++) {
- View child = mClockContainer.getChildAt(i);
- if (mVisibleInDoze.contains(child)) {
- continue;
- }
- child.setAlpha(dark ? 0 : 1);
- }
- if (mOwnerInfo != null) {
- mOwnerInfo.setAlpha(dark ? 0 : 1);
- }
-
- final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount);
- updateDozeVisibleViews();
- mKeyguardSlice.setDark(darkAmount);
- mClockView.setTextColor(blendedTextColor);
- mClockSeparator.setBackgroundTintList(ColorStateList.valueOf(blendedTextColor));
+ updateDark();
}
- public void setPulsing(boolean pulsing) {
+ private void updateDark() {
+ boolean dark = mDarkAmount == 1;
+ mLogoutView.setAlpha(dark ? 0 : 1);
+ if (mOwnerInfo != null) {
+ boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText());
+ mOwnerInfo.setVisibility(hasText && mDarkAmount != 1 ? VISIBLE : GONE);
+ }
+
+ final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ updateDozeVisibleViews();
+ mKeyguardSlice.setDarkAmount(mDarkAmount);
+ mClockView.setTextColor(blendedTextColor);
+ mClockSeparator.setBackgroundColor(blendedTextColor);
+ }
+
+ public void setPulsing(boolean pulsing, boolean animate) {
mPulsing = pulsing;
- mKeyguardSlice.setHideContent(pulsing);
- onSliceContentChanged(mKeyguardSlice.hasHeader());
+ mKeyguardSlice.setPulsing(pulsing, animate);
updateDozeVisibleViews();
}
@@ -378,4 +429,24 @@
Log.e(TAG, "Failed to logout user", re);
}
}
+
+ private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements
+ ViewClippingUtil.ClippingParameters {
+
+ ClipChildrenAnimationListener() {
+ ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */,
+ this /* clippingParams */);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */,
+ this /* clippingParams */);
+ }
+
+ @Override
+ public boolean shouldFinish(View view) {
+ return view == getParent();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d24675c..fb13925 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,8 +29,11 @@
import static android.os.BatteryManager.EXTRA_PLUGGED;
import static android.os.BatteryManager.EXTRA_STATUS;
+import android.annotation.AnyThread;
+import android.annotation.MainThread;
import android.app.ActivityManager;
import android.app.AlarmManager;
+import android.app.Instrumentation;
import android.app.PendingIntent;
import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
@@ -44,7 +47,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
-import android.graphics.Bitmap;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -77,6 +79,7 @@
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -355,6 +358,7 @@
private static int sCurrentUser;
private Runnable mUpdateFingerprintListeningState = this::updateFingerprintListeningState;
+ private static boolean sDisableHandlerCheckForTesting;
public synchronized static void setCurrentUser(int currentUser) {
sCurrentUser = currentUser;
@@ -366,6 +370,7 @@
@Override
public void onTrustChanged(boolean enabled, int userId, int flags) {
+ checkIsHandlerThread();
mUserHasTrust.put(userId, enabled);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -383,7 +388,7 @@
dispatchErrorMessage(message);
}
- protected void handleSimSubscriptionInfoChanged() {
+ private void handleSimSubscriptionInfoChanged() {
if (DEBUG_SIM_STATES) {
Log.v(TAG, "onSubscriptionInfoChanged()");
List<SubscriptionInfo> sil = mSubscriptionManager.getActiveSubscriptionInfoList();
@@ -451,6 +456,7 @@
@Override
public void onTrustManagedChanged(boolean managed, int userId) {
+ checkIsHandlerThread();
mUserTrustIsManaged.put(userId, managed);
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -638,6 +644,7 @@
}
private void notifyFingerprintRunningStateChanged() {
+ checkIsHandlerThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -646,6 +653,7 @@
}
}
private void handleFaceUnlockStateChanged(boolean running, int userId) {
+ checkIsHandlerThread();
mUserFaceUnlockRunning.put(userId, running);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -705,6 +713,7 @@
}
private void notifyStrongAuthStateChanged(int userId) {
+ checkIsHandlerThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1125,20 +1134,6 @@
updateFingerprintListeningState();
}
- /**
- * IMPORTANT: Must be called from UI thread.
- */
- public void dispatchSetBackground(Bitmap bmp) {
- if (DEBUG) Log.d(TAG, "dispatchSetBackground");
- final int count = mCallbacks.size();
- for (int i = 0; i < count; i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onSetBackground(bmp);
- }
- }
- }
-
private void handleUserInfoChanged(int userId) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1350,6 +1345,7 @@
* @param hasLockscreenWallpaper Whether Keyguard has a lockscreen wallpaper.
*/
public void setHasLockscreenWallpaper(boolean hasLockscreenWallpaper) {
+ checkIsHandlerThread();
if (hasLockscreenWallpaper != mHasLockscreenWallpaper) {
mHasLockscreenWallpaper = hasLockscreenWallpaper;
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
@@ -1371,7 +1367,7 @@
/**
* Handle {@link #MSG_DPM_STATE_CHANGED}
*/
- protected void handleDevicePolicyManagerStateChanged() {
+ private void handleDevicePolicyManagerStateChanged() {
updateFingerprintListeningState();
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1384,7 +1380,7 @@
/**
* Handle {@link #MSG_USER_SWITCHING}
*/
- protected void handleUserSwitching(int userId, IRemoteCallback reply) {
+ private void handleUserSwitching(int userId, IRemoteCallback reply) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1400,7 +1396,7 @@
/**
* Handle {@link #MSG_USER_SWITCH_COMPLETE}
*/
- protected void handleUserSwitchComplete(int userId) {
+ private void handleUserSwitchComplete(int userId) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1422,7 +1418,7 @@
/**
* Handle {@link #MSG_BOOT_COMPLETED}
*/
- protected void handleBootCompleted() {
+ private void handleBootCompleted() {
if (mBootCompleted) return;
mBootCompleted = true;
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1444,7 +1440,7 @@
/**
* Handle {@link #MSG_DEVICE_PROVISIONED}
*/
- protected void handleDeviceProvisioned() {
+ private void handleDeviceProvisioned() {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1461,7 +1457,7 @@
/**
* Handle {@link #MSG_PHONE_STATE_CHANGED}
*/
- protected void handlePhoneStateChanged(String newState) {
+ private void handlePhoneStateChanged(String newState) {
if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) {
mPhoneState = TelephonyManager.CALL_STATE_IDLE;
@@ -1481,7 +1477,7 @@
/**
* Handle {@link #MSG_RINGER_MODE_CHANGED}
*/
- protected void handleRingerModeChange(int mode) {
+ private void handleRingerModeChange(int mode) {
if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
mRingMode = mode;
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1526,8 +1522,8 @@
* Handle {@link #MSG_SIM_STATE_CHANGE}
*/
@VisibleForTesting
- protected void handleSimStateChange(int subId, int slotId, State state) {
-
+ void handleSimStateChange(int subId, int slotId, State state) {
+ checkIsHandlerThread();
if (DEBUG_SIM_STATES) {
Log.d(TAG, "handleSimStateChange(subId=" + subId + ", slotId="
+ slotId + ", state=" + state +")");
@@ -1590,6 +1586,7 @@
* <p>Needs to be called from the main thread.
*/
public void onKeyguardVisibilityChanged(boolean showing) {
+ checkIsHandlerThread();
if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
mKeyguardIsVisible = showing;
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1680,6 +1677,7 @@
* @param callback The callback to remove
*/
public void removeCallback(KeyguardUpdateMonitorCallback callback) {
+ checkIsHandlerThread();
if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback);
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
if (mCallbacks.get(i).get() == callback) {
@@ -1694,6 +1692,7 @@
* @param callback The callback to register
*/
public void registerCallback(KeyguardUpdateMonitorCallback callback) {
+ checkIsHandlerThread();
if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
// Prevent adding duplicate callbacks
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1712,6 +1711,7 @@
return mSwitchingUser;
}
+ @AnyThread
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post if first
@@ -1755,6 +1755,7 @@
* NOTE: Because handleSimStateChange() invokes callbacks immediately without going
* through mHandler, this *must* be called from the UI thread.
*/
+ @MainThread
public void reportSimUnlocked(int subId) {
if (DEBUG_SIM_STATES) Log.v(TAG, "reportSimUnlocked(subId=" + subId + ")");
int slotId = SubscriptionManager.getSlotIndex(subId);
@@ -1773,6 +1774,7 @@
if (!bypassHandler) {
mHandler.obtainMessage(MSG_REPORT_EMERGENCY_CALL_ACTION).sendToTarget();
} else {
+ checkIsHandlerThread();
handleReportEmergencyCallAction();
}
}
@@ -1975,6 +1977,7 @@
}
private void updateLogoutEnabled() {
+ checkIsHandlerThread();
boolean logoutEnabled = mDevicePolicyManager.isLogoutEnabled();
if (mLogoutEnabled != logoutEnabled) {
mLogoutEnabled = logoutEnabled;
@@ -1987,6 +1990,36 @@
}
}
+ private void checkIsHandlerThread() {
+ if (sDisableHandlerCheckForTesting) {
+ return;
+ }
+ if (!mHandler.getLooper().isCurrentThread()) {
+ Log.wtf(TAG, "must call on mHandler's thread "
+ + mHandler.getLooper().getThread() + ", not " + Thread.currentThread());
+ }
+ }
+
+ /**
+ * Turn off the handler check for testing.
+ *
+ * This is necessary because currently tests are not too careful about which thread they call
+ * into this class on.
+ *
+ * Note that this must be called before scheduling any work involving KeyguardUpdateMonitor
+ * instances.
+ *
+ * TODO: fix the tests and remove this.
+ */
+ @VisibleForTesting
+ public static void disableHandlerCheckForTesting(Instrumentation instrumentation) {
+ Preconditions.checkNotNull(instrumentation, "Must only call this method in tests!");
+ // Don't need synchronization here *if* the callers follow the contract and call this only
+ // before scheduling work for KeyguardUpdateMonitor on other threads, because the scheduling
+ // of that work forces a happens-before relationship.
+ sDisableHandlerCheckForTesting = true;
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardUpdateMonitor state:");
pw.println(" SIM States:");
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 913e781..a265a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -28,6 +28,7 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
@@ -74,6 +75,7 @@
private boolean mEnforceBouncer = false;
private boolean mBouncerOn = false;
private boolean mSessionActive = false;
+ private boolean mIsTouchScreen = true;
private int mState = StatusBarState.SHADE;
private boolean mScreenOn;
private boolean mShowingAod;
@@ -236,6 +238,11 @@
// already sufficiently prevents false unlocks.
return false;
}
+ if (!mIsTouchScreen) {
+ // Unlocking with input devices besides the touchscreen should already be sufficiently
+ // anti-falsed.
+ return false;
+ }
return mHumanInteractionClassifier.isFalseTouch();
}
@@ -450,6 +457,9 @@
}
public void onTouchEvent(MotionEvent event, int width, int height) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mIsTouchScreen = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
+ }
if (mSessionActive && !mBouncerOn) {
mDataCollector.onTouchEvent(event, width, height);
mHumanInteractionClassifier.onTouchEvent(event);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index b0bda16..90140ff 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -80,7 +80,9 @@
}
boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
- if (messagePending || oldState == DozeMachine.State.INITIALIZED) {
+ boolean pulseEnding = oldState == DozeMachine.State.DOZE_PULSE_DONE
+ && newState == DozeMachine.State.DOZE_AOD;
+ if (messagePending || oldState == DozeMachine.State.INITIALIZED || pulseEnding) {
// During initialization, we hide the navigation bar. That is however only applied after
// a traversal; setting the screen state here is immediate however, so it can happen
// that the screen turns on again before the navigation bar is hidden. To work around
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index a7975d7..087d481 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1380,6 +1380,15 @@
mHardwareLayout = HardwareUiLayout.get(mListView);
mHardwareLayout.setOutsideTouchListener(view -> dismiss());
setTitle(R.string.global_actions);
+ mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(
+ View host, AccessibilityEvent event) {
+ // Populate the title here, just as Activity does
+ event.getText().add(mContext.getString(R.string.global_actions));
+ return true;
+ }
+ });
}
private void updateList() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ca92d35..6809e76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -165,6 +165,7 @@
private static final int NOTIFY_SCREEN_TURNED_ON = 15;
private static final int NOTIFY_SCREEN_TURNED_OFF = 16;
private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17;
+ private static final int SYSTEM_READY = 18;
/**
* The default amount of time we stay awake (used for all key input)
@@ -775,6 +776,10 @@
* Let us know that the system is ready after startup.
*/
public void onSystemReady() {
+ mHandler.obtainMessage(SYSTEM_READY).sendToTarget();
+ }
+
+ private void handleSystemReady() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
@@ -1603,6 +1608,9 @@
Log.w(TAG, "Timeout while waiting for activity drawn!");
Trace.endSection();
break;
+ case SYSTEM_READY:
+ handleSystemReady();
+ break;
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 1149ad1..79fea9f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -21,6 +21,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -364,14 +366,7 @@
if (mStableInsets.isEmpty()) {
SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
}
- int position = (int) (mState.mRatioPositionBeforeMinimized *
- (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
- mSnapAlgorithm = null;
- initializeSnapAlgorithm();
-
- // Set the snap target before minimized but do not save until divider is attached and not
- // minimized because it does not know its minimized state yet.
- mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+ repositionSnapTargetBeforeMinimized();
}
public WindowManagerProxy getWindowManagerProxy() {
@@ -878,9 +873,36 @@
}
public void notifyDockSideChanged(int newDockSide) {
+ int oldDockSide = mDockSide;
mDockSide = newDockSide;
mMinimizedShadow.setDockSide(mDockSide);
requestLayout();
+
+ // Update the snap position to the new docked side with correct insets
+ SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+ mMinimizedSnapAlgorithm = null;
+ initializeSnapAlgorithm();
+
+ if (oldDockSide == DOCKED_LEFT && mDockSide == DOCKED_RIGHT
+ || oldDockSide == DOCKED_RIGHT && mDockSide == DOCKED_LEFT) {
+ repositionSnapTargetBeforeMinimized();
+ }
+
+ // Landscape to seascape rotation requires minimized to resize docked app correctly
+ if (mHomeStackResizable && mDockedStackMinimized) {
+ resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
+ }
+ }
+
+ private void repositionSnapTargetBeforeMinimized() {
+ int position = (int) (mState.mRatioPositionBeforeMinimized *
+ (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
+ mSnapAlgorithm = null;
+ initializeSnapAlgorithm();
+
+ // Set the snap target before minimized but do not save until divider is attached and not
+ // minimized because it does not know its minimized state yet.
+ mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
}
private void updateDisplayInfo() {
@@ -962,6 +984,12 @@
if (mHomeStackResizable && mIsInMinimizeInteraction) {
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
mDockedTaskRect);
+
+ // Move a right-docked-app to line up with the divider while dragging it
+ if (mDockSide == DOCKED_RIGHT) {
+ mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
+ - mDockedTaskRect.left + mDividerSize, 0);
+ }
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
@@ -976,6 +1004,12 @@
calculateBoundsForPosition(isHorizontalDivision() ? mDisplayHeight : mDisplayWidth,
mDockSide, mDockedTaskRect);
}
+
+ // Move a docked app if from the right in position with the divider up to insets
+ if (mDockSide == DOCKED_RIGHT) {
+ mDockedTaskRect.offset(Math.max(position,
+ mStableInsets.left) - mDockedTaskRect.left, 0);
+ }
calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
mOtherTaskRect);
mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java
new file mode 100644
index 0000000..742005d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * A TextView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaOptimizedTextView extends TextView {
+ public AlphaOptimizedTextView(Context context) {
+ super(context);
+ }
+
+ public AlphaOptimizedTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlphaOptimizedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlphaOptimizedTextView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DndSuppressingNotificationsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DndSuppressingNotificationsView.java
new file mode 100644
index 0000000..db3a02d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DndSuppressingNotificationsView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.IntegerRes;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.graphics.drawable.Icon;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.stack.ExpandableViewState;
+import com.android.systemui.statusbar.stack.StackScrollState;
+
+public class DndSuppressingNotificationsView extends StackScrollerDecorView {
+
+ private TextView mText;
+ private @StringRes int mTextId = R.string.dnd_suppressing_shade_text;
+
+ public DndSuppressingNotificationsView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mText.setText(mTextId);
+ }
+
+ @Override
+ protected View findContentView() {
+ return findViewById(R.id.hidden_container);
+ }
+
+ @Override
+ protected View findSecondaryView() {
+ return null;
+ }
+
+ public void setColor(@ColorInt int color) {
+ mText.setTextColor(color);
+ }
+
+ public void setOnContentClickListener(OnClickListener listener) {
+ mText.setOnClickListener(listener);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mText = findViewById(R.id.hidden_notifications);
+ }
+
+ @Override
+ public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+ return new DndSuppressingViewState();
+ }
+
+ public class DndSuppressingViewState extends ExpandableViewState {
+ @Override
+ public void applyToView(View view) {
+ super.applyToView(view);
+ if (view instanceof DndSuppressingNotificationsView) {
+ DndSuppressingNotificationsView dndView = (DndSuppressingNotificationsView) view;
+ boolean visible = this.clipTopAmount <= mText.getPaddingTop() * 0.6f;
+ dndView.performVisibilityAnimation(visible && !dndView.willBeGone());
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 42c774e..3efeb6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -76,6 +76,7 @@
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.HybridNotificationView;
+import com.android.systemui.statusbar.notification.NotificationCounters;
import com.android.systemui.statusbar.notification.NotificationInflater;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.NotificationViewWrapper;
@@ -1252,6 +1253,8 @@
Dependency.get(NotificationBlockingHelperManager.class);
boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
+ Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
+
// Continue with dismiss since we don't want the blocking helper to be directly associated
// with a certain notification.
performDismiss(fromAccessibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 65fb2da..a581049 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -30,6 +30,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -270,7 +271,8 @@
/** Animates out the guts view via either a fade or a circular reveal. */
- private void animateClose(int x, int y, boolean shouldDoCircularReveal) {
+ @VisibleForTesting
+ void animateClose(int x, int y, boolean shouldDoCircularReveal) {
if (shouldDoCircularReveal) {
// Circular reveal originating at (x, y)
if (x == -1 || y == -1) {
@@ -340,7 +342,8 @@
}
}
- private void setExposed(boolean exposed, boolean needsFalsingProtection) {
+ @VisibleForTesting
+ void setExposed(boolean exposed, boolean needsFalsingProtection) {
final boolean wasExposed = mExposed;
mExposed = exposed;
mNeedsFalsingProtection = needsFalsingProtection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 6a1740c..ec49f43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -54,17 +54,20 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationCounters;
import java.util.List;
/**
- * The guts of a notification revealed when performing a long press.
+ * The guts of a notification revealed when performing a long press. This also houses the blocking
+ * helper affordance that allows a user to keep/stop notifications after swiping one away.
*/
public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
private static final String TAG = "InfoGuts";
private INotificationManager mINotificationManager;
private PackageManager mPm;
+ private MetricsLogger mMetricsLogger;
private String mPackageName;
private String mAppName;
@@ -84,17 +87,27 @@
private OnAppSettingsClickListener mAppSettingsClickListener;
private NotificationGuts mGutsContainer;
- /** Whether this view is being shown as part of the blocking helper */
+ /** Whether this view is being shown as part of the blocking helper. */
private boolean mIsForBlockingHelper;
private boolean mNegativeUserSentiment;
- private OnClickListener mOnKeepShowing = this::closeControls;
+ /** Counter tag that describes how the user exit or quit out of this view. */
+ private String mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_DISMISSED;
+
+ private OnClickListener mOnKeepShowing = v -> {
+ mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
+ closeControls(v);
+ };
private OnClickListener mOnStopOrMinimizeNotifications = v -> {
+ mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
swapContent(false);
};
private OnClickListener mOnUndo = v -> {
+ // Reset exit counter that we'll log and record an undo event separately (not an exit event)
+ mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_DISMISSED;
+ logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
swapContent(true);
};
@@ -151,6 +164,7 @@
boolean isUserSentimentNegative)
throws RemoteException {
mINotificationManager = iNotificationManager;
+ mMetricsLogger = Dependency.get(MetricsLogger.class);
mPackageName = pkg;
mNumUniqueChannelsInRow = numUniqueChannelsInRow;
mSbn = sbn;
@@ -183,6 +197,8 @@
bindHeader();
bindPrompt();
bindButtons();
+
+ logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_SHOWN);
}
private void bindHeader() throws RemoteException {
@@ -235,6 +251,8 @@
final int appUidF = mAppUid;
settingsButton.setOnClickListener(
(View view) -> {
+ logBlockingHelperCounter(
+ NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
mOnSettingsClickListener.onClick(view,
mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
appUidF);
@@ -269,6 +287,13 @@
}
}
+ @VisibleForTesting
+ void logBlockingHelperCounter(String counterTag) {
+ if (mIsForBlockingHelper) {
+ mMetricsLogger.count(counterTag, 1);
+ }
+ }
+
private boolean hasImportanceChanged() {
return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance;
}
@@ -437,25 +462,15 @@
*/
@VisibleForTesting
void closeControls(View v) {
- if (mIsForBlockingHelper) {
- NotificationBlockingHelperManager manager =
- Dependency.get(NotificationBlockingHelperManager.class);
- manager.dismissCurrentBlockingHelper();
-
- // Since this won't get a callback via gutsContainer.closeControls, save the new
- // importance values immediately.
- saveImportance();
- } else {
- int[] parentLoc = new int[2];
- int[] targetLoc = new int[2];
- mGutsContainer.getLocationOnScreen(parentLoc);
- v.getLocationOnScreen(targetLoc);
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- final int x = targetLoc[0] - parentLoc[0] + centerX;
- final int y = targetLoc[1] - parentLoc[1] + centerY;
- mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
- }
+ int[] parentLoc = new int[2];
+ int[] targetLoc = new int[2];
+ mGutsContainer.getLocationOnScreen(parentLoc);
+ v.getLocationOnScreen(targetLoc);
+ final int centerX = v.getWidth() / 2;
+ final int centerY = v.getHeight() / 2;
+ final int x = targetLoc[0] - parentLoc[0] + centerX;
+ final int y = targetLoc[1] - parentLoc[1] + centerY;
+ mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
}
@Override
@@ -480,6 +495,7 @@
if (save) {
saveImportance();
}
+ logBlockingHelperCounter(mExitReasonCounter);
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java
new file mode 100644
index 0000000..9a12e8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification;
+
+/**
+ * Constants for counter tags for Notification-related actions/views.
+ */
+public class NotificationCounters {
+ /** Counter tag for notification dismissal. */
+ public static final String NOTIFICATION_DISMISSED = "notification_dismissed";
+
+ /** Counter tag for when the blocking helper is shown to the user. */
+ public static final String BLOCKING_HELPER_SHOWN = "blocking_helper_shown";
+ /** Counter tag for when the blocking helper is dismissed via a miscellaneous interaction. */
+ public static final String BLOCKING_HELPER_DISMISSED = "blocking_helper_dismissed";
+ /** Counter tag for when the user hits 'stop notifications' in the blocking helper. */
+ public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS =
+ "blocking_helper_stop_notifications";
+ /** Counter tag for when the user hits 'keep showing' in the blocking helper. */
+ public static final String BLOCKING_HELPER_KEEP_SHOWING =
+ "blocking_helper_keep_showing";
+ /**
+ * Counter tag for when the user hits undo in context of the blocking helper - this can happen
+ * multiple times per view.
+ */
+ public static final String BLOCKING_HELPER_UNDO = "blocking_helper_undo";
+ /** Counter tag for when the user hits the notification settings icon in the blocking helper. */
+ public static final String BLOCKING_HELPER_NOTIF_SETTINGS =
+ "blocking_helper_notif_settings";
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 264f574..4b66ee5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -55,6 +55,7 @@
private final UnlockMethodCache mUnlockMethodCache;
private AccessibilityController mAccessibilityController;
private boolean mHasFingerPrintIcon;
+ private boolean mHasFaceUnlockIcon;
private int mDensity;
private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
@@ -130,6 +131,7 @@
}
int state = getState();
boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
+ mHasFaceUnlockIcon = state == STATE_FACE_UNLOCK;
boolean useAdditionalPadding = anyFingerprintIcon;
boolean trustHidden = anyFingerprintIcon;
if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
@@ -179,6 +181,11 @@
setRestingAlpha(
anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT);
setImageDrawable(icon, false);
+ if (mHasFaceUnlockIcon) {
+ announceForAccessibility(getContext().getString(
+ R.string.accessibility_scanning_face));
+ }
+
mHasFingerPrintIcon = anyFingerprintIcon;
if (animation != null && isAnim) {
animation.forceAnimationOnUI();
@@ -228,6 +235,11 @@
info.addAction(unlock);
info.setHintText(getContext().getString(
R.string.accessibility_waiting_for_fingerprint));
+ } else if (mHasFaceUnlockIcon){
+ //Avoid 'button' to be spoken for scanning face
+ info.setClassName(LockIcon.class.getName());
+ info.setContentDescription(getContext().getString(
+ R.string.accessibility_scanning_face));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a0a97c5..8bb73da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -199,6 +199,7 @@
private ValueAnimator mQsSizeChangeAnimator;
private boolean mShowEmptyShadeView;
+ private boolean mShowDndView;
private boolean mQsScrimEnabled = true;
private boolean mLastAnnouncementWasQuickSettings;
@@ -392,6 +393,8 @@
if (mQs != null) {
mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
mQsMaxExpansionHeight = mQs.getDesiredHeight();
+ mNotificationStackScroller.setMaxTopPadding(
+ mQsMaxExpansionHeight + mQsNotificationTopPadding);
}
positionClockAndNotifications();
if (mQsExpanded && mQsFullyExpanded) {
@@ -588,6 +591,19 @@
mNotificationStackScroller.resetScrollPosition();
}
+ @Override
+ public void collapse(boolean delayed, float speedUpFactor) {
+ if (!canPanelBeCollapsed()) {
+ return;
+ }
+
+ if (mQsExpanded) {
+ mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
+ }
+ super.collapse(delayed, speedUpFactor);
+ }
+
public void closeQs() {
cancelQsAnimation();
setQsExpansion(mQsMinExpansionHeight);
@@ -615,6 +631,7 @@
public void expandWithQs() {
if (mQsExpansionEnabled) {
mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
}
expand(true /* animate */);
}
@@ -878,6 +895,7 @@
&& event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1);
mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
requestPanelHeightUpdate();
// Normally, we start listening when the panel is expanded, but here we need to start
@@ -1321,7 +1339,9 @@
protected void updateQsExpansion() {
if (mQs == null) return;
- mQs.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation());
+ float qsExpansionFraction = getQsExpansionFraction();
+ mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
+ mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
}
private String determineAccessibilityPaneTitle() {
@@ -1357,7 +1377,6 @@
} else if (mQsSizeChangeAnimator != null) {
return (int) mQsSizeChangeAnimator.getAnimatedValue();
} else if (mKeyguardShowing) {
-
// We can only do the smoother transition on Keyguard when we also are not collapsing
// from a scrolled quick settings.
return interpolate(getQsExpansionFraction(),
@@ -1527,7 +1546,6 @@
// On Keyguard, interpolate the QS expansion linearly to the panel expansion
t = expandedHeight / (getMaxPanelHeight());
} else {
-
// In Shade, interpolate linearly such that QS is closed whenever panel height is
// minimum QS expansion + minStackHeight
float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding()
@@ -1582,8 +1600,8 @@
// When only empty shade view is visible in QS collapsed state, simulate that we would have
// it in expanded QS state as well so we don't run into troubles when fading the view in/out
// and expanding/collapsing the whole panel from/to quick settings.
- if (mNotificationStackScroller.getNotGoneChildCount() == 0
- && mShowEmptyShadeView) {
+ if ((mNotificationStackScroller.getNotGoneChildCount() == 0
+ && mShowEmptyShadeView) || mShowDndView) {
notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
}
int maxQsHeight = mQsMaxExpansionHeight;
@@ -1776,6 +1794,7 @@
setListening(true);
}
mQsExpandImmediate = false;
+ mNotificationStackScroller.setShouldShowShelfOnly(false);
mTwoFingerQsExpandPossible = false;
mIsExpansionFromHeadsUp = false;
notifyListenersTrackingHeadsUp(null);
@@ -1827,6 +1846,7 @@
super.onTrackingStarted();
if (mQsFullyExpanded) {
mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
}
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
@@ -1866,7 +1886,7 @@
if (view == null && mQsExpanded) {
return;
}
- if (needsAnimation) {
+ if (needsAnimation && mDarkAmount == 0) {
mAnimateNextPositionUpdate = true;
}
ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
@@ -1894,6 +1914,8 @@
if (mAccessibilityManager.isEnabled()) {
setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
}
+ mNotificationStackScroller.setMaxTopPadding(
+ mQsMaxExpansionHeight + mQsNotificationTopPadding);
}
@Override
@@ -2222,13 +2244,17 @@
return mDozing;
}
+ public void showDndView(boolean dndViewVisible) {
+ mShowDndView = dndViewVisible;
+ mNotificationStackScroller.updateDndView(mShowDndView && !mQsExpanded);
+ }
+
public void showEmptyShadeView(boolean emptyShadeViewVisible) {
mShowEmptyShadeView = emptyShadeViewVisible;
updateEmptyShadeView();
}
private void updateEmptyShadeView() {
-
// Hide "No notifications" in QS.
mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
}
@@ -2652,10 +2678,13 @@
public void setPulsing(boolean pulsing) {
mPulsing = pulsing;
- mKeyguardStatusView.setPulsing(pulsing);
- positionClockAndNotifications();
- mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
- + mKeyguardStatusView.getClockBottom());
+ final boolean canAnimatePulse =
+ !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
+ if (canAnimatePulse) {
+ mAnimateNextPositionUpdate = true;
+ }
+ mNotificationStackScroller.setPulsing(pulsing, canAnimatePulse);
+ mKeyguardStatusView.setPulsing(pulsing, canAnimatePulse);
}
public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1d64088..57a3556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,10 +20,13 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
+import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager
.NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -166,6 +169,7 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.qs.QSFragment;
@@ -178,6 +182,7 @@
import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -185,6 +190,7 @@
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.DndSuppressingNotificationsView;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -207,14 +213,12 @@
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -412,7 +416,7 @@
protected NotificationViewHierarchyManager mViewHierarchyManager;
protected AppOpsListener mAppOpsListener;
protected KeyguardViewMediator mKeyguardViewMediator;
- private ZenModeController mZenController;
+ protected ZenModeController mZenController;
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -877,6 +881,7 @@
mVisualStabilityManager.setVisibilityLocationProvider(mStackScroller);
inflateEmptyShadeView();
+ inflateDndView();
inflateFooterView();
mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
@@ -1144,6 +1149,7 @@
protected void reevaluateStyles() {
inflateFooterView();
updateFooter();
+ inflateDndView();
inflateEmptyShadeView();
updateEmptyShadeView();
}
@@ -1165,6 +1171,19 @@
mStackScroller.setEmptyShadeView(mEmptyShadeView);
}
+ private void inflateDndView() {
+ if (mStackScroller == null) {
+ return;
+ }
+ mDndView = (DndSuppressingNotificationsView) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_dnd_suppressing_notifications, mStackScroller, false);
+ mDndView.setOnContentClickListener(v -> {
+ Intent intent = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+ startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ });
+ mStackScroller.setDndView(mDndView);
+ }
+
private void inflateFooterView() {
if (mStackScroller == null) {
return;
@@ -1339,8 +1358,15 @@
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
- ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
+ final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
+ if (navbarPos == NAV_BAR_POS_INVALID) {
+ return false;
+ }
+ int createMode = navbarPos == NAV_BAR_POS_LEFT
+ ? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
+ : ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+ return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode,
+ null, metricsDockAction);
} else {
Divider divider = getComponent(Divider.class);
if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
@@ -1452,9 +1478,11 @@
@VisibleForTesting
protected void updateFooter() {
boolean showFooterView = mState != StatusBarState.KEYGUARD
+ && !areNotificationsHidden()
&& mEntryManager.getNotificationData().getActiveNotifications().size() != 0
&& !mRemoteInputManager.getController().isRemoteInputActive();
boolean showDismissView = mClearAllEnabled && mState != StatusBarState.KEYGUARD
+ && !areNotificationsHidden()
&& hasActiveClearableNotifications();
mStackScroller.updateFooterView(showFooterView, showDismissView);
@@ -1477,10 +1505,13 @@
return false;
}
- private void updateEmptyShadeView() {
- boolean showEmptyShadeView =
- mState != StatusBarState.KEYGUARD &&
- mEntryManager.getNotificationData().getActiveNotifications().size() == 0;
+ @VisibleForTesting
+ protected void updateEmptyShadeView() {
+ boolean showDndView = mState != StatusBarState.KEYGUARD && areNotificationsHidden();
+ boolean showEmptyShadeView = !showDndView
+ && mState != StatusBarState.KEYGUARD
+ && mEntryManager.getNotificationData().getActiveNotifications().size() == 0;
+ mNotificationPanel.showDndView(showDndView);
mNotificationPanel.showEmptyShadeView(showEmptyShadeView);
}
@@ -1896,6 +1927,13 @@
}
}
+ if (!panelsEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey());
+ }
+ return false;
+ }
+
if (sbn.getNotification().fullScreenIntent != null) {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
@@ -4973,6 +5011,7 @@
protected NotificationShelf mNotificationShelf;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
+ protected DndSuppressingNotificationsView mDndView;
protected AssistManager mAssistManager;
@@ -5457,6 +5496,11 @@
mStackScroller.getChildCount() - offsetFromEnd++);
}
+ if (mDndView != null) {
+ mStackScroller.changeViewPosition(mDndView,
+ mStackScroller.getChildCount() - offsetFromEnd++);
+ }
+
mStackScroller.changeViewPosition(mEmptyShadeView,
mStackScroller.getChildCount() - offsetFromEnd++);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 897d13b..5ca5752 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -69,6 +69,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.ExpandHelper;
@@ -80,6 +81,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.DndSuppressingNotificationsView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
@@ -168,6 +170,7 @@
private int mCollapsedSize;
private int mPaddingBetweenElements;
private int mIncreasedPaddingBetweenElements;
+ private int mMaxTopPadding;
private int mRegularTopPadding;
private int mDarkTopPadding;
// Current padding, will be either mRegularTopPadding or mDarkTopPadding
@@ -176,6 +179,7 @@
private int mDarkSeparatorPadding;
private int mBottomMargin;
private int mBottomInset = 0;
+ private float mQsExpansionFraction;
/**
* The algorithm which calculates the properties for our children
@@ -229,9 +233,11 @@
private boolean mPanelTracking;
private boolean mExpandingNotification;
private boolean mExpandedInThisMotion;
+ private boolean mShouldShowShelfOnly;
protected boolean mScrollingEnabled;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
+ protected DndSuppressingNotificationsView mDndView;
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
@@ -370,6 +376,7 @@
private boolean mGroupExpandedForMeasure;
private boolean mScrollable;
private View mForcedScroll;
+ private View mNeedingPulseAnimation;
private float mDarkAmount = 0f;
private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
@@ -406,7 +413,6 @@
private final int mSeparatorWidth;
private final int mSeparatorThickness;
private final Rect mBackgroundAnimationRect = new Rect();
- private int mClockBottom;
private int mAntiBurnInOffsetX;
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
@@ -717,11 +723,7 @@
}
private void updateAlgorithmHeightAndPadding() {
- if (mPulsing) {
- mTopPadding = mClockBottom;
- } else {
- mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
- }
+ mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
mAmbientState.setLayoutHeight(getLayoutHeight());
updateAlgorithmLayoutMinHeight();
mAmbientState.setTopPadding(mTopPadding);
@@ -886,7 +888,20 @@
float appearFraction = 1.0f;
if (height >= appearEndPosition) {
translationY = 0;
- stackHeight = (int) height;
+ if (mShouldShowShelfOnly) {
+ stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
+ } else if (mQsExpanded) {
+ int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
+ int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
+ if (stackStartPosition <= stackEndPosition) {
+ stackHeight = stackEndPosition;
+ } else {
+ stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
+ stackEndPosition, mQsExpansionFraction);
+ }
+ } else {
+ stackHeight = (int) height;
+ }
} else {
appearFraction = getAppearFraction(height);
if (appearFraction >= 0) {
@@ -993,7 +1008,8 @@
private float getAppearEndPosition() {
int appearPosition;
int notGoneChildCount = getNotGoneChildCount();
- if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
+ if ((mEmptyShadeView.getVisibility() == GONE && mDndView.getVisibility() == GONE)
+ && notGoneChildCount != 0) {
if (isHeadsUpTransition()
|| (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
appearPosition = getTopHeadsUpPinnedHeight();
@@ -1003,6 +1019,8 @@
appearPosition += mShelf.getIntrinsicHeight();
}
}
+ } else if (mEmptyShadeView.getVisibility() == GONE) {
+ appearPosition = mDndView.getHeight();
} else {
appearPosition = mEmptyShadeView.getHeight();
}
@@ -2584,6 +2602,10 @@
setExpandedHeight(mExpandedHeight);
}
+ public void setMaxTopPadding(int maxTopPadding) {
+ mMaxTopPadding = maxTopPadding;
+ }
+
public int getLayoutMinHeight() {
if (isHeadsUpTransition()) {
return getTopHeadsUpPinnedHeight();
@@ -2591,19 +2613,6 @@
return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
}
- public int getFirstChildIntrinsicHeight() {
- final ExpandableView firstChild = getFirstChildNotGone();
- int firstChildMinHeight = firstChild != null
- ? firstChild.getIntrinsicHeight()
- : mEmptyShadeView != null
- ? mEmptyShadeView.getIntrinsicHeight()
- : mCollapsedSize;
- if (mOwnScrollY > 0) {
- firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
- }
- return firstChildMinHeight;
- }
-
public float getTopPaddingOverflow() {
return mTopPaddingOverflow;
}
@@ -3174,6 +3183,7 @@
generateViewResizeEvent();
generateGroupExpansionEvent();
generateAnimateEverythingEvent();
+ generatePulsingAnimationEvent();
mNeedsAnimation = false;
}
@@ -3900,6 +3910,7 @@
final int textColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor);
mFooterView.setTextColor(textColor);
mEmptyShadeView.setTextColor(textColor);
+ mDndView.setColor(textColor);
}
public void goToFullShade(long delay) {
@@ -3907,6 +3918,7 @@
mFooterView.setInvisible();
}
mEmptyShadeView.setInvisible();
+ mDndView.setInvisible();
mGoToFullShadeNeedsAnimation = true;
mGoToFullShadeDelay = delay;
mNeedsAnimation = true;
@@ -4058,25 +4070,39 @@
int newVisibility = visible ? VISIBLE : GONE;
boolean changedVisibility = oldVisibility != newVisibility;
- if (changedVisibility || newVisibility != GONE) {
+ if (changedVisibility) {
if (newVisibility != GONE) {
- int oldText = mEmptyShadeView.getTextResource();
- int newText;
- if (mStatusBar.areNotificationsHidden()) {
- newText = R.string.dnd_suppressing_shade_text;
- } else {
- newText = R.string.empty_shade_text;
- }
- if (changedVisibility || !Objects.equals(oldText, newText)) {
- mEmptyShadeView.setText(newText);
- showFooterView(mEmptyShadeView);
- }
+ showFooterView(mEmptyShadeView);
} else {
hideFooterView(mEmptyShadeView, true);
}
}
}
+ public void setDndView(DndSuppressingNotificationsView dndView) {
+ int index = -1;
+ if (mDndView != null) {
+ index = indexOfChild(mDndView);
+ removeView(mDndView);
+ }
+ mDndView = dndView;
+ addView(mDndView, index);
+ }
+
+ public void updateDndView(boolean visible) {
+ int oldVisibility = mDndView.willBeGone() ? GONE : mDndView.getVisibility();
+ int newVisibility = visible ? VISIBLE : GONE;
+
+ boolean changedVisibility = oldVisibility != newVisibility;
+ if (changedVisibility) {
+ if (newVisibility != GONE) {
+ showFooterView(mDndView);
+ } else {
+ hideFooterView(mDndView, true);
+ }
+ }
+ }
+
public void updateFooterView(boolean visible, boolean showDismissView) {
if (mFooterView == null) {
return;
@@ -4101,10 +4127,16 @@
} else {
footerView.setInvisible();
}
- footerView.setVisibility(VISIBLE);
- footerView.setWillBeGone(false);
- updateContentHeight();
- notifyHeightChangeListener(footerView);
+ Runnable onShowFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ footerView.setVisibility(VISIBLE);
+ footerView.setWillBeGone(false);
+ updateContentHeight();
+ notifyHeightChangeListener(footerView);
+ }
+ };
+ footerView.performVisibilityAnimation(true, onShowFinishRunnable);
}
private void hideFooterView(StackScrollerDecorView footerView, boolean isButtonVisible) {
@@ -4428,18 +4460,28 @@
return mIsExpanded;
}
- public void setPulsing(boolean pulsing, int clockBottom) {
+ public void setPulsing(boolean pulsing, boolean animated) {
if (!mPulsing && !pulsing) {
return;
}
mPulsing = pulsing;
- mClockBottom = clockBottom;
+ mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
mAmbientState.setPulsing(pulsing);
updateNotificationAnimationStates();
updateAlgorithmHeightAndPadding();
updateContentHeight();
- notifyHeightChangeListener(mShelf);
requestChildrenUpdate();
+ notifyHeightChangeListener(null, animated);
+ mNeedsAnimation |= animated;
+ }
+
+ private void generatePulsingAnimationEvent() {
+ if (mNeedingPulseAnimation != null) {
+ int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
+ : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
+ mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
+ mNeedingPulseAnimation = null;
+ }
}
public void setFadingOut(boolean fadingOut) {
@@ -4476,6 +4518,10 @@
updateAlgorithmLayoutMinHeight();
}
+ public void setQsExpansionFraction(float qsExpansionFraction) {
+ mQsExpansionFraction = qsExpansionFraction;
+ }
+
public void setOwnScrollY(int ownScrollY) {
if (ownScrollY != mOwnScrollY) {
// We still want to call the normal scrolled changed for accessibility reasons
@@ -4511,6 +4557,11 @@
}
}
+ public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
+ mShouldShowShelfOnly = shouldShowShelfOnly;
+ updateAlgorithmLayoutMinHeight();
+ }
+
public int getMinExpansionHeight() {
return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
}
@@ -4566,7 +4617,8 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
- + " alpha:%f scrollY:%d]",
+ + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
+ + " qsExpandFraction=%f]",
this.getClass().getSimpleName(),
mPulsing ? "T":"f",
mAmbientState.isQsCustomizerShowing() ? "T":"f",
@@ -4574,7 +4626,10 @@
: getVisibility() == View.GONE ? "gone"
: "invisible",
getAlpha(),
- mAmbientState.getScrollY()));
+ mAmbientState.getScrollY(),
+ mMaxTopPadding,
+ mShouldShowShelfOnly ? "T":"f",
+ mQsExpansionFraction));
}
public boolean isFullyDark() {
@@ -5007,6 +5062,16 @@
.animateTopInset()
.animateY()
.animateZ(),
+
+ // ANIMATION_TYPE_PULSE_APPEAR
+ new AnimationFilter()
+ .animateAlpha()
+ .animateY(),
+
+ // ANIMATION_TYPE_PULSE_DISAPPEAR
+ new AnimationFilter()
+ .animateAlpha()
+ .animateY(),
};
static int[] LENGTHS = new int[] {
@@ -5067,6 +5132,12 @@
// ANIMATION_TYPE_EVERYTHING
StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_PULSE_APPEAR
+ KeyguardSliceView.DEFAULT_ANIM_DURATION,
+
+ // ANIMATION_TYPE_PULSE_DISAPPEAR
+ KeyguardSliceView.DEFAULT_ANIM_DURATION / 2,
};
static final int ANIMATION_TYPE_ADD = 0;
@@ -5088,6 +5159,8 @@
static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
static final int ANIMATION_TYPE_EVERYTHING = 18;
+ static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
+ static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index c80bdc6..d01db14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -60,6 +60,7 @@
public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
private final int mGoToFullShadeAppearingTranslation;
+ private final int mPulsingAppearingTranslation;
private final ExpandableViewState mTmpState = new ExpandableViewState();
private final AnimationProperties mAnimationProperties;
public NotificationStackScrollLayout mHostLayout;
@@ -90,6 +91,9 @@
mGoToFullShadeAppearingTranslation =
hostLayout.getContext().getResources().getDimensionPixelSize(
R.dimen.go_to_full_shade_appearing_translation);
+ mPulsingAppearingTranslation =
+ hostLayout.getContext().getResources().getDimensionPixelSize(
+ R.dimen.pulsing_notification_appear_translation);
mAnimationProperties = new AnimationProperties() {
@Override
public AnimationFilter getAnimationFilter() {
@@ -427,6 +431,18 @@
ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
row.prepareExpansionChanged(finalState);
} else if (event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
+ ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ mTmpState.copyFrom(viewState);
+ mTmpState.yTranslation += mPulsingAppearingTranslation;
+ mTmpState.alpha = 0;
+ mTmpState.applyToView(changingView);
+ } else if (event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
+ ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ viewState.yTranslation += mPulsingAppearingTranslation;
+ viewState.alpha = 0;
+ } else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
// This item is added, initialize it's properties.
ExpandableViewState viewState = finalState.getViewStateForView(changingView);
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java
new file mode 100644
index 0000000..a54b0e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.util.wakelock;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.view.animation.Animation;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.util.Assert;
+
+public class KeepAwakeAnimationListener extends AnimatorListenerAdapter
+ implements Animation.AnimationListener {
+ @VisibleForTesting
+ static WakeLock sWakeLock;
+
+ public KeepAwakeAnimationListener(Context context) {
+ Assert.isMainThread();
+ if (sWakeLock == null) {
+ sWakeLock = WakeLock.createPartial(context, "animation");
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ onStart();
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ onEnd();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ onStart();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onEnd();
+ }
+
+ private void onStart() {
+ Assert.isMainThread();
+ sWakeLock.acquire();
+ }
+
+ private void onEnd() {
+ Assert.isMainThread();
+ sWakeLock.release();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index d7aedc4..b5071de 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -742,8 +742,11 @@
updateVolumeRowH(row);
}
updateRingerH();
- mWindow.setTitle(mContext.getString(R.string.volume_dialog_title,
- getStreamLabelH(getActiveRow().ss)));
+ mWindow.setTitle(composeWindowTitle());
+ }
+
+ CharSequence composeWindowTitle() {
+ return mContext.getString(R.string.volume_dialog_title, getStreamLabelH(getActiveRow().ss));
}
private void updateVolumeRowH(VolumeRow row) {
@@ -1214,6 +1217,13 @@
}
@Override
+ public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ // Activities populate their title here. Follow that example.
+ event.getText().add(composeWindowTitle());
+ return true;
+ }
+
+ @Override
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
AccessibilityEvent event) {
rescheduleTimeoutH();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 7686948..56bce5d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -16,39 +16,78 @@
package com.android.keyguard;
import android.graphics.Color;
+import android.net.Uri;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+import java.util.HashSet;
+
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+
@SmallTest
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner.class)
public class KeyguardSliceViewTest extends SysuiTestCase {
private KeyguardSliceView mKeyguardSliceView;
+ private Uri mSliceUri;
@Before
public void setUp() throws Exception {
mKeyguardSliceView = (KeyguardSliceView) LayoutInflater.from(getContext())
.inflate(R.layout.keyguard_status_area, null);
+ mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
+ SliceProvider.setSpecs(new HashSet<>(Collections.singletonList(SliceSpecs.LIST)));
+ }
+
+ @Test
+ public void showSlice_notifiesListener() {
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
+ boolean[] notified = {false};
+ mKeyguardSliceView.setContentChangeListener((hasHeader)-> {
+ notified[0] = true;
+ });
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertTrue("Listener should be notified about slice changes.", notified[0]);
+ }
+
+ @Test
+ public void hasHeader_readsSliceData() {
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
+
+ builder.setHeader((ListBuilder.HeaderBuilder headerBuilder) -> {
+ headerBuilder.setTitle("header title!");
+ });
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
}
@Test
public void getTextColor_whiteTextWhenAOD() {
// Set text color to red since the default is white and test would always pass
mKeyguardSliceView.setTextColor(Color.RED);
- mKeyguardSliceView.setDark(0);
+ mKeyguardSliceView.setDarkAmount(0);
Assert.assertEquals("Should be using regular text color", Color.RED,
mKeyguardSliceView.getTextColor());
- mKeyguardSliceView.setDark(1);
+ mKeyguardSliceView.setDarkAmount(1);
Assert.assertEquals("Should be using AOD text color", Color.WHITE,
mKeyguardSliceView.getTextColor());
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 7475cd7..a35ca46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -28,6 +28,8 @@
import android.testing.LeakCheck;
import android.util.Log;
+import com.android.keyguard.KeyguardUpdateMonitor;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -70,6 +72,7 @@
"SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
});
InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
+ KeyguardUpdateMonitor.disableHandlerCheckForTesting(inst);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index b0530c8..65fd7f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -33,9 +33,12 @@
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,7 +47,6 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
-import android.app.NotificationManager;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -52,7 +54,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
-import android.os.Looper;
+import android.os.IBinder;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
@@ -65,6 +67,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -100,7 +103,7 @@
private StatusBarNotification mSbn;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
- private Looper mLooper;
+ @Mock private MetricsLogger mMetricsLogger;
@Mock private INotificationManager mMockINotificationManager;
@Mock private PackageManager mMockPackageManager;
@Mock private NotificationBlockingHelperManager mBlockingHelperManager;
@@ -112,6 +115,7 @@
mBlockingHelperManager);
mTestableLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+ mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -301,6 +305,24 @@
}
@Test
+ public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+ mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
+ verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
+ }
+
+ @Test
+ public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
+ // Bind notification logs an event, so this counts as one invocation for the metrics logger.
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
+ true);
+ mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
+ verify(mMetricsLogger, times(2)).count(anyString(), anyInt());
+ }
+
+ @Test
public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -471,6 +493,13 @@
false /* isNonblockable */, true /* isForBlockingHelper */,
true /* isUserSentimentNegative */);
+ NotificationGuts guts = spy(new NotificationGuts(mContext, null));
+ when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
+ doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean());
+ doNothing().when(guts).setExposed(anyBoolean(), anyBoolean());
+ guts.setGutsContent(mNotificationInfo);
+ mNotificationInfo.setGutsParent(guts);
+
mNotificationInfo.findViewById(R.id.keep).performClick();
verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
@@ -495,6 +524,9 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
false /* isUserSentimentNegative */);
+ NotificationGuts guts = mock(NotificationGuts.class);
+ doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
+ mNotificationInfo.setGutsParent(guts);
mNotificationInfo.closeControls(mNotificationInfo);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d3cb5a6..41cf869 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -78,6 +78,7 @@
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DndSuppressingNotificationsView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.FooterViewButton;
@@ -103,6 +104,7 @@
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import org.junit.Before;
@@ -132,6 +134,7 @@
@Mock private ScrimController mScrimController;
@Mock private ArrayList<Entry> mNotificationList;
@Mock private FingerprintUnlockController mFingerprintUnlockController;
+ @Mock private ZenModeController mZenController;
@Mock private NotificationData mNotificationData;
// Mock dependencies:
@@ -163,6 +166,7 @@
mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
mDependency.injectTestDependency(AppOpsListener.class, mock(AppOpsListener.class));
+ mDependency.injectTestDependency(ZenModeController.class, mZenController);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -213,7 +217,7 @@
mRemoteInputManager, mock(NotificationGroupManager.class),
mock(FalsingManager.class), mock(StatusBarWindowManager.class),
mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
- mock(NotificationShelf.class), mLockscreenUserManager,
+ mock(NotificationShelf.class), mLockscreenUserManager, mZenController,
mock(CommandQueue.class));
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
@@ -443,6 +447,30 @@
}
@Test
+ public void testPeek_disabledStatusBar() {
+ Notification n = new Notification.Builder(getContext(), "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+ UserHandle.of(0), null, 0);
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
+
+ assertFalse("The panel shouldn't allow peek while disabled",
+ mStatusBar.shouldPeek(entry, sbn));
+ }
+
+ @Test
+ public void testPeek_disabledNotificationShade() {
+ Notification n = new Notification.Builder(getContext(), "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+ UserHandle.of(0), null, 0);
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
+
+ assertFalse("The panel shouldn't allow peek while notitifcation shade disabled",
+ mStatusBar.shouldPeek(entry, sbn));
+ }
+
+ @Test
public void testLogHidden() {
try {
mStatusBar.handleVisibleToUserChanged(false);
@@ -652,6 +680,60 @@
}
@Test
+ public void testDNDView_atEnd() {
+ // add footer
+ mStatusBar.reevaluateStyles();
+
+ // add notification
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ mStackScroller.addContainerView(row);
+
+ mStatusBar.onUpdateRowStates();
+
+ // move dnd view to end
+ verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
+ verify(mStackScroller).changeViewPosition(any(DndSuppressingNotificationsView.class),
+ eq(-2 /* end */));
+ }
+
+ @Test
+ public void updateEmptyShade_nonNotificationsDndOff() {
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ when(mNotificationData.getActiveNotifications()).thenReturn(new ArrayList<>());
+ assertEquals(0, mEntryManager.getNotificationData().getActiveNotifications().size());
+
+ mStatusBar.updateEmptyShadeView();
+ verify(mNotificationPanelView).showDndView(false);
+ verify(mNotificationPanelView).showEmptyShadeView(true);
+ }
+
+ @Test
+ public void updateEmptyShade_noNotificationsDndOn() {
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ when(mNotificationData.getActiveNotifications()).thenReturn(new ArrayList<>());
+ assertEquals(0, mEntryManager.getNotificationData().getActiveNotifications().size());
+ when(mZenController.areNotificationsHiddenInShade()).thenReturn(true);
+
+ mStatusBar.updateEmptyShadeView();
+ verify(mNotificationPanelView).showDndView(true);
+ verify(mNotificationPanelView).showEmptyShadeView(false);
+ }
+
+ @Test
+ public void updateEmptyShade_yesNotificationsDndOff() {
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ ArrayList<Entry> entries = new ArrayList<>();
+ entries.add(mock(Entry.class));
+ when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+ assertEquals(1, mEntryManager.getNotificationData().getActiveNotifications().size());
+ when(mZenController.areNotificationsHiddenInShade()).thenReturn(false);
+
+ mStatusBar.updateEmptyShadeView();
+ verify(mNotificationPanelView).showDndView(false);
+ verify(mNotificationPanelView).showEmptyShadeView(false);
+ }
+
+ @Test
public void testSetState_changesIsFullScreenUserSwitcherState() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
assertFalse(mStatusBar.isFullScreenUserSwitcherState());
@@ -697,6 +779,7 @@
DozeScrimController dozeScrimController,
NotificationShelf notificationShelf,
NotificationLockscreenUserManager notificationLockscreenUserManager,
+ ZenModeController zenController,
CommandQueue commandQueue) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
@@ -725,6 +808,7 @@
mDozeScrimController = dozeScrimController;
mNotificationShelf = notificationShelf;
mLockscreenUserManager = notificationLockscreenUserManager;
+ mZenController = zenController;
mCommandQueue = commandQueue;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index eeb4209..3d17ec4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -18,6 +18,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -34,6 +35,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.TestableDependency;
+import com.android.systemui.statusbar.DndSuppressingNotificationsView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.NotificationBlockingHelperManager;
@@ -69,6 +71,7 @@
@Mock private NotificationGroupManager mGroupManager;
@Mock private ExpandHelper mExpandHelper;
@Mock private EmptyShadeView mEmptyShadeView;
+ @Mock private DndSuppressingNotificationsView mDndView;
@Before
@UiThreadTest
@@ -86,6 +89,7 @@
mStackScroller.setHeadsUpManager(mHeadsUpManager);
mStackScroller.setGroupManager(mGroupManager);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
+ mStackScroller.setDndView(mDndView);
// Stub out functionality that isn't necessary to test.
doNothing().when(mBar)
@@ -120,40 +124,6 @@
}
@Test
- public void updateEmptyView_dndSuppressing() {
- when(mEmptyShadeView.willBeGone()).thenReturn(true);
- when(mBar.areNotificationsHidden()).thenReturn(true);
-
- mStackScroller.updateEmptyShadeView(true);
-
- verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
- }
-
- @Test
- public void updateEmptyView_dndNotSuppressing() {
- mStackScroller.setEmptyShadeView(mEmptyShadeView);
- when(mEmptyShadeView.willBeGone()).thenReturn(true);
- when(mBar.areNotificationsHidden()).thenReturn(false);
-
- mStackScroller.updateEmptyShadeView(true);
-
- verify(mEmptyShadeView).setText(R.string.empty_shade_text);
- }
-
- @Test
- public void updateEmptyView_noNotificationsToDndSuppressing() {
- mStackScroller.setEmptyShadeView(mEmptyShadeView);
- when(mEmptyShadeView.willBeGone()).thenReturn(true);
- when(mBar.areNotificationsHidden()).thenReturn(false);
- mStackScroller.updateEmptyShadeView(true);
- verify(mEmptyShadeView).setText(R.string.empty_shade_text);
-
- when(mBar.areNotificationsHidden()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true);
- verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
- }
-
- @Test
@UiThreadTest
public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
mStackScroller.setExpandedHeight(0f);
@@ -173,7 +143,7 @@
mStackScroller.updateFooterView(true, false);
- verify(view).setVisibility(View.VISIBLE);
+ verify(view).performVisibilityAnimation(eq(true), any());
verify(view).performSecondaryVisibilityAnimation(false);
}
@@ -186,7 +156,7 @@
mStackScroller.updateFooterView(true, true);
- verify(view).setVisibility(View.VISIBLE);
+ verify(view).performVisibilityAnimation(eq(true), any());
verify(view).performSecondaryVisibilityAnimation(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
new file mode 100644
index 0000000..43942f7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.util.wakelock;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.animation.Animator;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner.class)
+public class KeepAwakeAnimationListenerTest extends SysuiTestCase {
+ @Mock WakeLock mWakeLock;
+ KeepAwakeAnimationListener mKeepAwakeAnimationListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ KeepAwakeAnimationListener.sWakeLock = mWakeLock;
+ mKeepAwakeAnimationListener = new KeepAwakeAnimationListener(getContext());
+ }
+
+ @Test
+ public void onAnimationStart_holdsWakeLock() {
+ mKeepAwakeAnimationListener.onAnimationStart((Animator) null);
+ verify(mWakeLock).acquire();
+ verify(mWakeLock, never()).release();
+
+ mKeepAwakeAnimationListener.onAnimationEnd((Animator) null);
+ verify(mWakeLock).release();
+ }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 5d8a065..0ee6236 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5717,6 +5717,29 @@
// OS: P
SETTINGS_ZEN_NOTIFICATIONS = 1400;
+ // An event category for slices.
+ // OPEN: Slice became visible.
+ // CLOSE: Slice became invisible.
+ // ACTION: Slice was tapped.
+ SLICE = 1401;
+
+ // The authority part of the slice URI
+ FIELD_SLICE_AUTHORITY = 1402;
+
+ // The path part of the slice URI
+ FIELD_SLICE_PATH = 1403;
+
+ // The authority part of the subslice URI
+ FIELD_SUBSLICE_AUTHORITY = 1404;
+
+ // The path part of the subslice URI
+ FIELD_SUBSLICE_PATH = 1405;
+
+ // OPEN: DND onboarding activity > don't update button
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACTION_ZEN_ONBOARDING_KEEP_CURRENT_SETTINGS = 1406;
+
// ---- End P Constants, all P constants go above this line ----
// First Q constant in master goes here:
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 3253f2e..51c0488 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -219,6 +219,8 @@
final String activePackageName = getActiveAutofillServicePackageName();
if (packageName.equals(activePackageName)) {
removeCachedServiceLocked(getChangingUserId());
+ } else {
+ handlePackageUpdateLocked(packageName);
}
}
}
@@ -250,6 +252,8 @@
return true;
}
removeCachedServiceLocked(getChangingUserId());
+ } else {
+ handlePackageUpdateLocked(pkg);
}
}
}
@@ -274,6 +278,14 @@
}
return serviceComponent.getPackageName();
}
+
+ @GuardedBy("mLock")
+ private void handlePackageUpdateLocked(String packageName) {
+ final int size = mServicesCache.size();
+ for (int i = 0; i < size; i++) {
+ mServicesCache.valueAt(i).handlePackageUpdateLocked(packageName);
+ }
+ }
};
// package changes
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0bb29a7..e582daa 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -626,9 +626,25 @@
}
@GuardedBy("mLock")
+ void handlePackageUpdateLocked(String packageName) {
+ final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
+ if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
+ resetExtServiceLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void resetExtServiceLocked() {
+ if (sVerbose) Slog.v(TAG, "reset autofill service.");
+ mFieldClassificationStrategy.reset();
+ }
+
+ @GuardedBy("mLock")
void destroyLocked() {
if (sVerbose) Slog.v(TAG, "destroyLocked()");
+ resetExtServiceLocked();
+
final int numSessions = mSessions.size();
final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions);
for (int i = 0; i < numSessions; i++) {
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index da52201..9bec856 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -83,7 +83,7 @@
}
@Nullable
- private ServiceInfo getServiceInfo() {
+ ServiceInfo getServiceInfo() {
final String packageName =
mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
if (packageName == null) {
@@ -119,6 +119,18 @@
return name;
}
+ void reset() {
+ synchronized (mLock) {
+ if (mServiceConnection != null) {
+ if (sDebug) Slog.d(TAG, "reset(): unbinding service.");
+ mContext.unbindService(mServiceConnection);
+ mServiceConnection = null;
+ } else {
+ if (sDebug) Slog.d(TAG, "reset(): service is not bound. Do nothing.");
+ }
+ }
+ }
+
/**
* Run a command, starting the service connection if necessary.
*/
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 821cca1..44edabc 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -421,7 +421,7 @@
// after the app's agent runs to handle its private filesystem
// contents, back up any OBB content it has on its behalf.
- if (mIncludeObbs) {
+ if (mIncludeObbs && !isSharedStorage) {
boolean obbOkay = obbConnection.backupObbs(pkg, out);
if (!obbOkay) {
throw new RuntimeException("Failure writing OBB stack for " + pkg);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index aaa42e5..a4d0dc8 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -2411,19 +2411,26 @@
// after moving out of the inactive state, so no need to worry about that.
boolean becomeInactive = false;
if (mState != STATE_ACTIVE) {
- scheduleReportActiveLocked(type, Process.myUid());
+ // Motion shouldn't affect light state, if it's already in doze-light or maintenance
+ boolean lightIdle = mLightState == LIGHT_STATE_IDLE
+ || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK
+ || mLightState == LIGHT_STATE_IDLE_MAINTENANCE;
+ if (!lightIdle) {
+ // Only switch to active state if we're not in either idle state
+ scheduleReportActiveLocked(type, Process.myUid());
+ addEvent(EVENT_NORMAL, type);
+ }
mState = STATE_ACTIVE;
mInactiveTimeout = timeout;
mCurIdleBudget = 0;
mMaintenanceStartTime = 0;
EventLogTags.writeDeviceIdle(mState, type);
- addEvent(EVENT_NORMAL, type);
becomeInactive = true;
}
if (mLightState == LIGHT_STATE_OVERRIDE) {
// We went out of light idle mode because we had started deep idle mode... let's
// now go back and reset things so we resume light idling if appropriate.
- mLightState = STATE_ACTIVE;
+ mLightState = LIGHT_STATE_ACTIVE;
EventLogTags.writeDeviceIdleLight(mLightState, type);
becomeInactive = true;
}
@@ -2811,6 +2818,8 @@
pw.println(" If no DURATION is specified, 10 seconds is used");
pw.println(" If [-r] option is used, then the package is removed from temp whitelist "
+ "and any [-d] is ignored");
+ pw.println(" motion");
+ pw.println(" Simulate a motion event to bring the device out of deep doze");
}
class Shell extends ShellCommand {
@@ -3207,13 +3216,28 @@
}
} else {
synchronized (this) {
- for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
+ for (int j = 0; j < mPowerSaveWhitelistApps.size(); j++) {
pw.print(mPowerSaveWhitelistApps.keyAt(j));
pw.print(",");
pw.println(mPowerSaveWhitelistApps.valueAt(j));
}
}
}
+ } else if ("motion".equals(cmd)) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ synchronized (this) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ motionLocked();
+ pw.print("Light state: ");
+ pw.print(lightStateToString(mLightState));
+ pw.print(", deep state: ");
+ pw.println(stateToString(mState));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
} else {
return shell.handleDefaultCommands(cmd);
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 2465ba2..d869734 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -33,10 +33,11 @@
# It logs the time remaining before the device would've normally gone to sleep without the request.
2731 power_soft_sleep_requested (savedwaketimems|2)
# Power save state has changed. See BatterySaverController.java for the details.
-2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
+2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5),(reason|1|5)
27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
# Note when the user activity timeout has been overriden by ActivityManagerService
27391 user_activity_timeout_override (override|2|3)
+27392 battery_saver_setting (threshold|1)
#
# Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index 021bfaa..fe30057 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.os.Handler;
+import android.os.Looper;
import android.os.Trace;
/**
@@ -28,6 +29,9 @@
* to be delayed for a user-noticeable amount of time.
*/
public final class FgThread extends ServiceThread {
+ private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
+ private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
+
private static FgThread sInstance;
private static Handler sHandler;
@@ -39,7 +43,10 @@
if (sInstance == null) {
sInstance = new FgThread();
sInstance.start();
- sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ final Looper looper = sInstance.getLooper();
+ looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ looper.setSlowLogThresholdMs(
+ SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 41f413d..607db4e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -117,10 +117,10 @@
return (onSubscriptionsChangedListenerCallback != null);
}
- boolean canReadPhoneState() {
+ boolean canReadCallLog() {
try {
- return TelephonyPermissions.checkReadPhoneState(
- context, subId, callerPid, callerUid, callingPackage, "listen");
+ return TelephonyPermissions.checkReadCallLog(
+ context, subId, callerPid, callerUid, callingPackage);
} catch (SecurityException e) {
return false;
}
@@ -667,8 +667,8 @@
}
private String getCallIncomingNumber(Record record, int phoneId) {
- // Hide the number if record's process can't currently read phone state.
- return record.canReadPhoneState() ? mCallIncomingNumber[phoneId] : "";
+ // Only reveal the incoming number if the record has read call log permission.
+ return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
}
private Record add(IBinder binder) {
@@ -729,13 +729,13 @@
}
}
- public void notifyCallState(int state, String incomingNumber) {
+ public void notifyCallState(int state, String phoneNumber) {
if (!checkNotifyPermission("notifyCallState()")) {
return;
}
if (VDBG) {
- log("notifyCallState: state=" + state + " incomingNumber=" + incomingNumber);
+ log("notifyCallState: state=" + state + " phoneNumber=" + phoneNumber);
}
synchronized (mRecords) {
@@ -743,8 +743,10 @@
if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
(r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
- String incomingNumberOrEmpty = r.canReadPhoneState() ? incomingNumber : "";
- r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
+ // Ensure the listener has read call log permission; if they do not return
+ // an empty phone number.
+ String phoneNumberOrEmpty = r.canReadCallLog() ? phoneNumber : "";
+ r.callback.onCallStateChanged(state, phoneNumberOrEmpty);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -755,7 +757,7 @@
// Called only by Telecomm to communicate call state across different phone accounts. So
// there is no need to add a valid subId or slotId.
- broadcastCallStateChanged(state, incomingNumber,
+ broadcastCallStateChanged(state, phoneNumber,
SubscriptionManager.INVALID_PHONE_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
@@ -1571,9 +1573,6 @@
Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
intent.putExtra(PhoneConstants.STATE_KEY,
PhoneConstantConversions.convertCallState(state).toString());
- if (!TextUtils.isEmpty(incomingNumber)) {
- intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
- }
// If a valid subId was specified, we should fire off a subId-specific state
// change intent and include the subId.
@@ -1589,13 +1588,20 @@
// Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast.
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ Intent intentWithPhoneNumber = new Intent(intent);
+ if (!TextUtils.isEmpty(incomingNumber)) {
+ intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
+ }
// Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
// that have the runtime one
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ mContext.sendBroadcastAsUser(intentWithPhoneNumber, UserHandle.ALL,
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_PHONE_STATE,
AppOpsManager.OP_READ_PHONE_STATE);
+ mContext.sendBroadcastAsUserMultiplePermissions(intentWithPhoneNumber, UserHandle.ALL,
+ new String[] { android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_CALL_LOG});
}
private void broadcastDataConnectionStateChanged(int state,
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index f813074..b2fa684 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -28,6 +28,7 @@
*/
public final class UiThread extends ServiceThread {
private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
+ private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
private static UiThread sInstance;
private static Handler sHandler;
@@ -48,7 +49,8 @@
sInstance.start();
final Looper looper = sInstance.getLooper();
looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
- looper.setSlowDispatchThresholdMs(SLOW_DISPATCH_THRESHOLD_MS);
+ looper.setSlowLogThresholdMs(
+ SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 566e128..59093c1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -92,6 +92,7 @@
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
"android.hardware.audio@2.0::IDevicesFactory",
+ "android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.graphics.composer@2.1::IComposer",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d54fee8..c4d6ab5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5049,9 +5049,20 @@
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+ return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
+ resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
+ true /*validateIncomingUser*/);
+ }
+
+ public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
+ Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+ int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
+ boolean validateIncomingUser) {
enforceNotIsolatedCaller("startActivity");
- userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, false, ALLOW_FULL_ONLY, "startActivity", null);
+
+ userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
+ Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
+
// TODO: Switch to user app stacks here.
return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
@@ -26218,6 +26229,16 @@
}
@Override
+ public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
+ Intent intent, Bundle options, int userId) {
+ return ActivityManagerService.this.startActivityAsUser(
+ caller, callerPacakge, intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
+ false /*validateIncomingUser*/);
+ }
+
+ @Override
public int getUidProcessState(int uid) {
return getUidState(uid);
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 16c5969..e73f42f 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1626,6 +1626,10 @@
if (parent != null) {
parent.onActivityStateChanged(this, state, reason);
}
+
+ if (state == STOPPING) {
+ mWindowContainerController.notifyAppStopping();
+ }
}
ActivityState getState() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e5565dc..4ace689 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1315,10 +1315,6 @@
return aInfo;
}
- ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
- return resolveIntent(intent, resolvedType, userId, 0, Binder.getCallingUid());
- }
-
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
synchronized (mService) {
@@ -1330,9 +1326,19 @@
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
modifiedFlags |= PackageManager.MATCH_INSTANT;
}
- return mService.getPackageManagerInternalLocked().resolveIntent(
- intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
+ // In order to allow cross-profile lookup, we clear the calling identity here.
+ // Note the binder identity won't affect the result, but filterCallingUid will.
+
+ // Cross-user/profile call check are done at the entry points
+ // (e.g. AMS.startActivityAsUser).
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mService.getPackageManagerInternalLocked().resolveIntent(
+ intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
} finally {
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -2409,6 +2415,16 @@
if (stack.isCompatible(windowingMode, activityType)) {
return stack;
}
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
+ && display.getSplitScreenPrimaryStack() == stack
+ && candidateTask == stack.topTask()) {
+ // This is a special case when we try to launch an activity that is currently on
+ // top of split-screen primary stack, but is targeting split-screen secondary.
+ // In this case we don't want to move it to another stack.
+ // TODO(b/78788972): Remove after differentiating between preferred and required
+ // launch options.
+ return stack;
+ }
}
}
@@ -2890,10 +2906,8 @@
try {
ActivityRecord r = stack.topRunningActivityLocked();
Rect insetBounds = null;
- if (tempPinnedTaskBounds != null) {
- // We always use 0,0 as the position for the inset rect because
- // if we are getting insets at all in the pinned stack it must mean
- // we are headed for fullscreen.
+ if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
+ // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
insetBounds = tempRect;
insetBounds.top = 0;
insetBounds.left = 0;
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 31ccf35..5e29d10 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -233,7 +233,7 @@
* ensures {@code targetUserId} is a real user ID and not a special user ID such as
* {@link android.os.UserHandle#USER_ALL}, etc.
*/
- private int checkTargetUser(int targetUserId, boolean validateIncomingUser,
+ int checkTargetUser(int targetUserId, boolean validateIncomingUser,
int realCallingPid, int realCallingUid, String reason) {
if (validateIncomingUser) {
return mService.mUserController.handleIncomingUser(realCallingPid, realCallingUid,
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 5b6b508..8c3ff34 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -203,7 +203,7 @@
mResolvedType = null;
final UserInfo parent = mUserManager.getProfileParent(mUserId);
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
}
@@ -223,9 +223,11 @@
final UserInfo parent = mUserManager.getProfileParent(mUserId);
if (parent != null) {
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
+ mRealCallingUid);
} else {
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId);
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
+ mRealCallingUid);
}
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
@@ -244,7 +246,8 @@
final Intent moreDetailsIntent = new Intent(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS)
.setPackage(suspendingPackage);
final String requiredPermission = Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS;
- final ResolveInfo resolvedInfo = mSupervisor.resolveIntent(moreDetailsIntent, null, userId);
+ final ResolveInfo resolvedInfo = mSupervisor.resolveIntent(moreDetailsIntent, null, userId,
+ 0, mRealCallingUid);
if (resolvedInfo != null && resolvedInfo.activityInfo != null
&& requiredPermission.equals(resolvedInfo.activityInfo.permission)) {
moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage)
@@ -276,7 +279,7 @@
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, 0);
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
}
@@ -309,7 +312,7 @@
}
final UserInfo parent = mUserManager.getProfileParent(mUserId);
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
}
@@ -362,7 +365,7 @@
mCallingUid = mRealCallingUid;
mResolvedType = null;
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId);
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 7ff7d9a..fb4107c 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -791,7 +791,7 @@
callingUid = realCallingUid;
callingPid = realCallingPid;
- rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+ rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0, realCallingUid);
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
null /*profilerInfo*/);
@@ -952,6 +952,9 @@
mSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
boolean componentSpecified = intent.getComponent() != null;
+ final int realCallingPid = Binder.getCallingPid();
+ final int realCallingUid = Binder.getCallingUid();
+
// Save a copy in case ephemeral needs it
final Intent ephemeralIntent = new Intent(intent);
// Don't modify the client's object!
@@ -969,7 +972,8 @@
componentSpecified = false;
}
- ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+ ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+ 0 /* matchFlags */, realCallingUid);
if (rInfo == null) {
UserInfo userInfo = mSupervisor.getUserInfo(userId);
if (userInfo != null && userInfo.isManagedProfile()) {
@@ -991,7 +995,7 @@
rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- Binder.getCallingUid());
+ realCallingUid);
}
}
}
@@ -999,8 +1003,6 @@
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
synchronized (mService) {
- final int realCallingPid = Binder.getCallingPid();
- final int realCallingUid = Binder.getCallingUid();
int callingPid;
if (callingUid >= 0) {
callingPid = -1;
@@ -1073,7 +1075,8 @@
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
componentSpecified = true;
- rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId);
+ rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
+ 0 /* matchFlags */, realCallingUid);
aInfo = rInfo != null ? rInfo.activityInfo : null;
if (aInfo != null) {
aInfo = mService.getActivityInfoForUser(aInfo, userId);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 483fec6..e0aa2a2 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -283,6 +283,14 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
+ // Extract options before clearing calling identity
+ SafeActivityOptions mergedOptions = key.options;
+ if (mergedOptions == null) {
+ mergedOptions = SafeActivityOptions.fromBundle(options);
+ } else {
+ mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
+ }
+
final long origId = Binder.clearCallingIdentity();
if (whitelistDuration != null) {
@@ -319,13 +327,6 @@
switch (key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
try {
- SafeActivityOptions mergedOptions = key.options;
- if (mergedOptions == null) {
- mergedOptions = SafeActivityOptions.fromBundle(options);
- } else {
- mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
- }
-
// Note when someone has a pending intent, even from different
// users, then there's no need to ensure the calling user matches
// the target user, so validateIncomingUser is always false below.
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index 2de75273..0fb69e7 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -32,6 +32,7 @@
import android.content.pm.ActivityInfo;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Process;
import android.os.UserHandle;
import android.util.Slog;
import android.view.RemoteAnimationAdapter;
@@ -78,6 +79,9 @@
mOriginalCallingPid = Binder.getCallingPid();
mOriginalCallingUid = Binder.getCallingUid();
mOriginalOptions = options;
+ if (mOriginalCallingPid == Process.myPid()) {
+ Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
+ }
}
/**
@@ -89,6 +93,9 @@
mRealCallingPid = Binder.getCallingPid();
mRealCallingUid = Binder.getCallingUid();
mCallerOptions = options;
+ if (mRealCallingPid == Process.myPid()) {
+ Slog.wtf(TAG, "setCallerOptions called after clearing calling id");
+ }
}
/**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index db3efab..324df41 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5671,8 +5671,6 @@
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
- sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
setBluetoothA2dpOnInt(true, eventSource);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
@@ -5940,12 +5938,6 @@
// address is not used for now, but may be used when multiple a2dp devices are supported
synchronized (mA2dpAvrcpLock) {
mAvrcpAbsVolSupported = support;
- sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
- mStreamStates[AudioSystem.STREAM_MUSIC], 0);
- sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
- mStreamStates[AudioSystem.STREAM_RING], 0);
}
}
@@ -7015,6 +7007,7 @@
final int LOG_NB_EVENTS_WIRED_DEV_CONNECTION = 30;
final int LOG_NB_EVENTS_FORCE_USE = 20;
final int LOG_NB_EVENTS_VOLUME = 40;
+ final int LOG_NB_EVENTS_DYN_POLICY = 10;
final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
"phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
@@ -7031,6 +7024,9 @@
final private AudioEventLogger mVolumeLogger = new AudioEventLogger(LOG_NB_EVENTS_VOLUME,
"volume changes (logged when command received by AudioService)");
+ final private AudioEventLogger mDynPolicyLogger = new AudioEventLogger(LOG_NB_EVENTS_DYN_POLICY,
+ "dynamic policy events (logged when command received by AudioService)");
+
private static final String[] RINGER_MODE_NAMES = new String[] {
"SILENT",
"VIBRATE",
@@ -7098,6 +7094,7 @@
pw.print(" mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
dumpAudioPolicies(pw);
+ mDynPolicyLogger.dump(pw);
mPlaybackMonitor.dump(pw);
@@ -7422,8 +7419,6 @@
boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
- if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder()
- + " with config:" + policyConfig);
String regId = null;
// error handling
boolean hasPermissionForPolicy =
@@ -7435,6 +7430,8 @@
return null;
}
+ mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for "
+ + pcb.asBinder() + " with config:" + policyConfig)).printLog(TAG));
synchronized (mAudioPolicies) {
try {
if (mAudioPolicies.containsKey(pcb.asBinder())) {
@@ -7457,7 +7454,8 @@
}
public void unregisterAudioPolicyAsync(IAudioPolicyCallback pcb) {
- if (DEBUG_AP) Log.d(TAG, "unregisterAudioPolicyAsync for " + pcb.asBinder());
+ mDynPolicyLogger.log((new AudioEventLogger.StringEvent("unregisterAudioPolicyAsync for "
+ + pcb.asBinder()).printLog(TAG)));
synchronized (mAudioPolicies) {
AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder());
if (app == null) {
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 34309b6..2feea41 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -103,6 +103,9 @@
conf.dump(pw);
}
}
+ pw.println("\n");
+ // log
+ sEventLogger.dump(pw);
}
private ArrayList<AudioRecordingConfiguration> anonymizeForPublicConsumption(
@@ -190,6 +193,9 @@
case AudioManager.RECORD_CONFIG_EVENT_STOP:
// return failure if an unknown recording session stopped
configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
+ if (configChanged) {
+ sEventLogger.log(new RecordingEvent(event, uid, session, source, null));
+ }
break;
case AudioManager.RECORD_CONFIG_EVENT_START:
final AudioFormat clientFormat = new AudioFormat.Builder()
@@ -231,6 +237,9 @@
mRecordConfigs.put(sessionKey, updatedConfig);
configChanged = true;
}
+ if (configChanged) {
+ sEventLogger.log(new RecordingEvent(event, uid, session, source, packageName));
+ }
break;
default:
Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
@@ -281,4 +290,36 @@
mDispatcherCb.asBinder().unlinkToDeath(this, 0);
}
}
+
+ /**
+ * Inner class for recording event logging
+ */
+ private static final class RecordingEvent extends AudioEventLogger.Event {
+ private final int mRecEvent;
+ private final int mClientUid;
+ private final int mSession;
+ private final int mSource;
+ private final String mPackName;
+
+ RecordingEvent(int event, int uid, int session, int source, String packName) {
+ mRecEvent = event;
+ mClientUid = uid;
+ mSession = session;
+ mSource = source;
+ mPackName = packName;
+ }
+
+ @Override
+ public String eventToString() {
+ return new StringBuilder("rec ").append(
+ mRecEvent == AudioManager.RECORD_CONFIG_EVENT_START ? "start" : "stop ")
+ .append(" uid:").append(mClientUid)
+ .append(" session:").append(mSession)
+ .append(" src:").append(MediaRecorder.toLogFriendlyAudioSource(mSource))
+ .append(mPackName == null ? "" : " pack:" + mPackName).toString();
+ }
+ }
+
+ private static final AudioEventLogger sEventLogger = new AudioEventLogger(50,
+ "recording activity as reported through AudioSystem.AudioRecordingCallback");
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index e840a29..03d8f39 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1279,7 +1279,9 @@
case Process.SYSTEM_UID:
break; // Okay
default:
- throw new SecurityException("Invalid extras specified.");
+ final String msg = "Invalid extras specified.";
+ Log.w(TAG, msg + " requestsync -f/-F needs to run on 'adb shell'");
+ throw new SecurityException(msg);
}
}
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index a55870f..5fa4245 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -68,6 +68,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -454,6 +455,7 @@
}
};
+ private final HandlerThread mThread;
private final SyncHandler mSyncHandler;
private final SyncManagerConstants mConstants;
@@ -604,7 +606,9 @@
mSyncAdapters = new SyncAdaptersCache(mContext);
- mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
+ mThread = new HandlerThread("SyncManager", android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ mThread.start();
+ mSyncHandler = new SyncHandler(mThread.getLooper());
mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
@Override
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index f403953..0425844 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -316,7 +316,7 @@
return true;
}
- private void resetShortTermModel() {
+ public void resetShortTermModel() {
mBrightnessMapper.clearUserDataPoints();
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 46e883c..3b35d02 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -774,14 +774,8 @@
brightness = mScreenBrightnessForVr;
}
- boolean setBrightnessToOverride = false;
if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
brightness = mPowerRequest.screenBrightnessOverride;
- // If there's a screen brightness override, we want to reset the brightness to it
- // whenever the user changes it, to communicate that these changes aren't taking
- // effect. However, for a nicer user experience, we don't do it here, but rather after
- // the temporary brightness has been taken into account.
- setBrightnessToOverride = true;
}
final boolean autoBrightnessEnabledInDoze =
@@ -804,12 +798,6 @@
brightnessIsTemporary = true;
}
- // Reset the brightness to the screen brightness override to communicate to the user that
- // her changes aren't taking effect.
- if (setBrightnessToOverride && !brightnessIsTemporary) {
- putScreenBrightnessSetting(brightness);
- }
-
final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
if (autoBrightnessAdjustmentChanged) {
mTemporaryAutoBrightnessAdjustment = Float.NaN;
@@ -1452,6 +1440,9 @@
if (userSwitch) {
// Don't treat user switches as user initiated change.
mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.resetShortTermModel();
+ }
}
mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
// We don't bother with a pending variable for VR screen brightness since we just
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
index 4100a9a..b935ba2 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
@@ -80,7 +80,7 @@
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
- mSuccessVibrationEffect = getSuccessVibrationEffect(context);
+ mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
try {
if (token != null) {
@@ -239,25 +239,4 @@
vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
}
}
-
- private static VibrationEffect getSuccessVibrationEffect(Context ctx) {
- int[] arr = ctx.getResources().getIntArray(
- com.android.internal.R.array.config_longPressVibePattern);
- final long[] vibePattern;
- if (arr == null || arr.length == 0) {
- vibePattern = DEFAULT_SUCCESS_VIBRATION_PATTERN;
- } else {
- vibePattern = new long[arr.length];
- for (int i = 0; i < arr.length; i++) {
- vibePattern[i] = arr[i];
- }
- }
- if (vibePattern.length == 1) {
- return VibrationEffect.createOneShot(
- vibePattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
- } else {
- return VibrationEffect.createWaveform(vibePattern, -1);
- }
- }
-
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 17f6d5c..9ee28d8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
@@ -711,7 +712,7 @@
StatusBarNotification sbn = r.sbn;
cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
- Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
+ FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
REASON_CLICK, nv.rank, nv.count, null);
nv.recycle();
reportUserInteraction(r);
@@ -755,7 +756,7 @@
}
}
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
true, userId, REASON_CANCEL, nv.rank, nv.count,null);
nv.recycle();
}
@@ -986,7 +987,7 @@
cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
record.sbn.getPackageName(), record.sbn.getTag(),
record.sbn.getId(), 0,
- Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
+ FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
REASON_TIMEOUT, null);
}
}
@@ -2085,7 +2086,7 @@
// Don't allow client applications to cancel foreground service notis or autobundled
// summaries.
final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
- (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
+ (FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
}
@@ -2100,7 +2101,7 @@
// Calling from user space, don't allow the canceling of actively
// running foreground services.
cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
- pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
+ pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,
REASON_APP_CANCEL_ALL, null);
}
@@ -2686,7 +2687,7 @@
private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
true,
userId, REASON_LISTENER_CANCEL, info);
}
@@ -3877,7 +3878,9 @@
for (int j = 0; j < listenerSize; j++) {
if (i > 0) pw.print(',');
final ManagedServiceInfo listener = listeners.valueAt(i);
- pw.print(listener.component);
+ if (listener != null) {
+ pw.print(listener.component);
+ }
}
}
pw.println(')');
@@ -3963,7 +3966,7 @@
// FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
// initially *and* force remove FLAG_FOREGROUND_SERVICE.
sbn.getNotification().flags =
- (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
+ (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
mRankingHelper.sort(mNotificationList);
mListeners.notifyPostedLocked(r, r);
}
@@ -4049,7 +4052,7 @@
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid));
- if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
+ if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0
&& (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
&& (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
// Increase the importance of foreground service notifications unless the user had an
@@ -4430,7 +4433,7 @@
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
notification.flags |=
- old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
r.setInterruptive(isVisuallyInterruptive(old, r));
}
@@ -4439,7 +4442,7 @@
// Ensure if this is a foreground service that the proper additional
// flags are set.
- if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
notification.flags |= Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR;
}
@@ -4507,6 +4510,13 @@
if (oldN.extras == null || newN.extras == null) {
return false;
}
+
+ // Ignore visual interruptions from foreground services because users
+ // consider them one 'session'. Count them for everything else.
+ if (r != null && (r.sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+
if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TITLE),
newN.extras.get(Notification.EXTRA_TITLE))) {
return true;
@@ -5810,7 +5820,7 @@
final StatusBarNotification childSbn = childR.sbn;
if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
childR.getGroupKey().equals(parentNotification.getGroupKey())
- && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
+ && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
&& (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
childSbn.getTag(), userId, 0, 0, reason, listenerName);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index febce31..7f141ee 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -116,17 +116,12 @@
private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>();
private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
- private final ArrayMap<Pair<String, Integer>, Boolean> mSystemAppCache = new ArrayMap<>();
private final Context mContext;
private final RankingHandler mRankingHandler;
private final PackageManager mPm;
private SparseBooleanArray mBadgingEnabled;
- private Signature[] mSystemSignature;
- private String mPermissionControllerPackageName;
- private String mServicesSystemSharedLibPackageName;
- private String mSharedSystemSharedLibPackageName;
private boolean mAreChannelsBypassingDnd;
private ZenModeHelper mZenModeHelper;
@@ -161,7 +156,6 @@
}
}
- getSignatures();
updateChannelsBypassingDnd();
}
@@ -623,7 +617,6 @@
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
throw new IllegalArgumentException("Reserved id");
}
- final boolean isSystemApp = isSystemPackage(pkg, uid);
NotificationChannel existing = r.channels.get(channel.getId());
// Keep most of the existing settings
if (existing != null && fromTargetApp) {
@@ -651,7 +644,7 @@
// system apps and dnd access apps can bypass dnd if the user hasn't changed any
// fields on the channel yet
- if (existing.getUserLockedFields() == 0 && (isSystemApp || hasDndAccess)) {
+ if (existing.getUserLockedFields() == 0 && hasDndAccess) {
boolean bypassDnd = channel.canBypassDnd();
existing.setBypassDnd(bypassDnd);
@@ -669,7 +662,7 @@
}
// Reset fields that apps aren't allowed to set.
- if (fromTargetApp && !(isSystemApp || hasDndAccess)) {
+ if (fromTargetApp && !hasDndAccess) {
channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
}
if (fromTargetApp) {
@@ -695,65 +688,6 @@
channel.unlockFields(channel.getUserLockedFields());
}
- /**
- * Determine whether a package is a "system package", in which case certain things (like
- * bypassing DND) should be allowed.
- */
- private boolean isSystemPackage(String pkg, int uid) {
- Pair<String, Integer> app = new Pair(pkg, uid);
- if (mSystemAppCache.containsKey(app)) {
- return mSystemAppCache.get(app);
- }
-
- PackageInfo pi;
- try {
- pi = mPm.getPackageInfoAsUser(
- pkg, PackageManager.GET_SIGNATURES, UserHandle.getUserId(uid));
- } catch (NameNotFoundException e) {
- Slog.w(TAG, "Can't find pkg", e);
- return false;
- }
- boolean isSystem = (mSystemSignature[0] != null
- && mSystemSignature[0].equals(getFirstSignature(pi)))
- || pkg.equals(mPermissionControllerPackageName)
- || pkg.equals(mServicesSystemSharedLibPackageName)
- || pkg.equals(mSharedSystemSharedLibPackageName)
- || pkg.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME)
- || isDeviceProvisioningPackage(pkg);
- mSystemAppCache.put(app, isSystem);
- return isSystem;
- }
-
- private Signature getFirstSignature(PackageInfo pkg) {
- if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
- return pkg.signatures[0];
- }
- return null;
- }
-
- private Signature getSystemSignature() {
- try {
- final PackageInfo sys = mPm.getPackageInfoAsUser(
- "android", PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
- return getFirstSignature(sys);
- } catch (NameNotFoundException e) {
- }
- return null;
- }
-
- private boolean isDeviceProvisioningPackage(String packageName) {
- String deviceProvisioningPackage = mContext.getResources().getString(
- com.android.internal.R.string.config_deviceProvisioningPackage);
- return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
- }
-
- private void getSignatures() {
- mSystemSignature = new Signature[]{getSystemSignature()};
- mPermissionControllerPackageName = mPm.getPermissionControllerPackageName();
- mServicesSystemSharedLibPackageName = mPm.getServicesSystemSharedLibraryPackageName();
- mSharedSystemSharedLibPackageName = mPm.getSharedSystemSharedLibraryPackageName();
- }
-
@Override
public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
boolean fromUser) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 156f702..658c7f1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -173,18 +173,6 @@
}
}
- public boolean shouldSuppressWhenScreenOff() {
- synchronized (mConfig) {
- return !mConfig.allowWhenScreenOff;
- }
- }
-
- public boolean shouldSuppressWhenScreenOn() {
- synchronized (mConfig) {
- return !mConfig.allowWhenScreenOn;
- }
- }
-
public void addCallback(Callback callback) {
mCallbacks.add(callback);
}
@@ -592,14 +580,12 @@
return;
}
pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b,"
- + "messages=%b,messagesFrom=%s,"
- + "events=%b,reminders=%b,whenScreenOff=%b,whenScreenOn=%b)\n",
+ + "messages=%b,messagesFrom=%s,events=%b,reminders=%b)\n",
config.allowAlarms, config.allowMedia, config.allowSystem,
config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
config.allowRepeatCallers, config.allowMessages,
ZenModeConfig.sourceToString(config.allowMessagesFrom),
- config.allowEvents, config.allowReminders, config.allowWhenScreenOff,
- config.allowWhenScreenOn);
+ config.allowEvents, config.allowReminders);
pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects);
pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule);
if (config.automaticRules.isEmpty()) return;
@@ -1185,7 +1171,7 @@
private void showZenUpgradeNotification(int zen) {
final boolean showNotification = mIsBootComplete
- && zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ && zen != Global.ZEN_MODE_OFF
&& Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
@@ -1204,17 +1190,20 @@
mContext.getResources().getString(R.string.global_action_settings));
int title = R.string.zen_upgrade_notification_title;
int content = R.string.zen_upgrade_notification_content;
+ int drawable = R.drawable.ic_zen_24dp;
if (NotificationManager.Policy.areAllVisualEffectsSuppressed(
getNotificationPolicy().suppressedVisualEffects)) {
title = R.string.zen_upgrade_notification_visd_title;
content = R.string.zen_upgrade_notification_visd_content;
+ drawable = R.drawable.ic_dnd_block_notifications;
}
+
Intent onboardingIntent = new Intent(Settings.ZEN_MODE_ONBOARDING);
onboardingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
return new Notification.Builder(mContext, SystemNotificationChannels.DO_NOT_DISTURB)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_settings_24dp)
- .setLargeIcon(Icon.createWithResource(mContext, R.drawable.ic_zen_24dp))
+ .setLargeIcon(Icon.createWithResource(mContext, drawable))
.setContentTitle(mContext.getResources().getString(title))
.setContentText(mContext.getResources().getString(content))
.setContentIntent(PendingIntent.getActivity(mContext, 0, onboardingIntent,
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 3b8a994..fd51be5 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -694,32 +694,25 @@
private final class OverlayChangeListener
implements OverlayManagerServiceImpl.OverlayChangeListener {
@Override
- public void onChanged(@NonNull final String targetPackageName, final int userId,
- boolean targetChanged, boolean overlayChanged) {
+ public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
schedulePersistSettings();
FgThread.getHandler().post(() -> {
- // Update the targets' overlays if a change to the target or an overlay occurs
- if (targetChanged || overlayChanged) {
- updateAssets(userId, targetPackageName);
+ updateAssets(userId, targetPackageName);
+
+ final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
+ Uri.fromParts("package", targetPackageName, null));
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ if (DEBUG) {
+ Slog.d(TAG, "send broadcast " + intent);
}
- // Create the broadcast if the overlay changes
- if (overlayChanged) {
- final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
- Uri.fromParts("package", targetPackageName, null));
- intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- if (DEBUG) {
- Slog.d(TAG, "send broadcast " + intent);
- }
-
- try {
- ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
- null, null, null, android.app.AppOpsManager.OP_NONE, null, false,
- false, userId);
- } catch (RemoteException e) {
- // Intentionally left empty.
- }
+ try {
+ ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
+ null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
+ userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
}
});
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index a487ae9..c57f97b 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -207,7 +207,9 @@
Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, 0);
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -215,7 +217,9 @@
Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, 0);
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
@@ -224,7 +228,9 @@
+ userId);
}
- updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING);
+ if (updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
@@ -232,7 +238,9 @@
Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, 0);
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
@@ -240,17 +248,21 @@
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, 0);
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
/**
- * Calls OverlayChangeListener#onChanged if the settings for the overlay target were modified,
- * and calls OverlayChangeListener#onTargetChanged to signal a change in the target package that
- * requires updating target overlays.
+ * Update the state of any overlays for this target.
+ *
+ * Returns true if the system should refresh the app's overlay paths (i.e.
+ * if the settings were modified for this target, or there is at least one
+ * enabled framework overlay).
*/
- private void updateAllOverlaysForTarget(@NonNull final String targetPackageName,
+ private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName,
final int userId, final int flags) {
- boolean overlayModified = false;
+ boolean modified = false;
final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
final int N = ois.size();
for (int i = 0; i < N; i++) {
@@ -258,19 +270,22 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
userId);
if (overlayPackage == null) {
- overlayModified |= mSettings.remove(oi.packageName, oi.userId);
+ modified |= mSettings.remove(oi.packageName, oi.userId);
removeIdmapIfPossible(oi);
} else {
try {
- overlayModified |= updateState(targetPackageName, oi.packageName, userId, flags);
+ modified |= updateState(targetPackageName, oi.packageName, userId, flags);
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
- overlayModified |= mSettings.remove(oi.packageName, userId);
+ modified |= mSettings.remove(oi.packageName, userId);
}
}
}
- mListener.onChanged(targetPackageName, userId, /* targetChanged */ true, overlayModified);
+ // check for enabled framework overlays
+ modified = modified || !getEnabledOverlayPackageNames("android", userId).isEmpty();
+
+ return modified;
}
void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
@@ -291,8 +306,7 @@
overlayPackage.overlayCategory);
try {
if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
- mListener.onChanged(overlayPackage.overlayTarget, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
}
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
@@ -308,8 +322,7 @@
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
if (updateState(oi.targetPackageName, packageName, userId, 0)) {
- mListener.onChanged(oi.targetPackageName, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(oi.targetPackageName, userId);
}
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
@@ -326,8 +339,7 @@
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
if (updateState(oi.targetPackageName, packageName, userId, FLAG_OVERLAY_IS_UPGRADING)) {
removeIdmapIfPossible(oi);
- mListener.onChanged(oi.targetPackageName, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(oi.targetPackageName, userId);
}
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
@@ -361,8 +373,7 @@
}
if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
- mListener.onChanged(pkg.overlayTarget, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(pkg.overlayTarget, userId);
}
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
@@ -376,8 +387,7 @@
removeIdmapIfPossible(overlayInfo);
if (overlayInfo.isEnabled()) {
// Only trigger updates if the overlay was enabled.
- mListener.onChanged(overlayInfo.targetPackageName, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
}
}
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -425,8 +435,7 @@
modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
if (modified) {
- mListener.onChanged(oi.targetPackageName, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(oi.targetPackageName, userId);
}
return true;
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -485,8 +494,7 @@
modified |= updateState(targetPackageName, packageName, userId, 0);
if (modified) {
- mListener.onChanged(targetPackageName, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(targetPackageName, userId);
}
return true;
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -519,8 +527,7 @@
}
if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
- mListener.onChanged(overlayPackage.overlayTarget, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
}
return true;
}
@@ -540,8 +547,7 @@
}
if (mSettings.setHighestPriority(packageName, userId)) {
- mListener.onChanged(overlayPackage.overlayTarget, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
}
return true;
}
@@ -561,8 +567,7 @@
}
if (mSettings.setLowestPriority(packageName, userId)) {
- mListener.onChanged(overlayPackage.overlayTarget, userId,
- /* targetChanged */ false, /* overlayChanged */ true);
+ mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
}
return true;
}
@@ -693,8 +698,7 @@
}
interface OverlayChangeListener {
- void onChanged(@NonNull String targetPackage, int userId,
- boolean targetChanged, boolean overlayChanged);
+ void onOverlaysChanged(@NonNull String targetPackage, int userId);
}
interface PackageManagerHelper {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 595de9e..feac8e6 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -21,6 +21,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -560,7 +561,7 @@
}
@Override
- public void startActivityAsUser(String callingPackage,
+ public void startActivityAsUser(IApplicationThread caller, String callingPackage,
ComponentName component, Rect sourceBounds,
Bundle opts, UserHandle user) throws RemoteException {
if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) {
@@ -574,6 +575,8 @@
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
launchIntent.setPackage(component.getPackageName());
+ boolean canLaunch = false;
+
final int callingUid = injectBinderCallingUid();
long ident = Binder.clearCallingIdentity();
try {
@@ -604,35 +607,42 @@
// this component so ok to launch.
launchIntent.setPackage(null);
launchIntent.setComponent(component);
- mContext.startActivityAsUser(launchIntent, opts, user);
- return;
+ canLaunch = true;
+ break;
}
}
- throw new SecurityException("Attempt to launch activity without "
- + " category Intent.CATEGORY_LAUNCHER " + component);
+ if (!canLaunch) {
+ throw new SecurityException("Attempt to launch activity without "
+ + " category Intent.CATEGORY_LAUNCHER " + component);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
+ mActivityManagerInternal.startActivityAsUser(caller, callingPackage,
+ launchIntent, opts, user.getIdentifier());
}
@Override
- public void showAppDetailsAsUser(String callingPackage, ComponentName component,
+ public void showAppDetailsAsUser(IApplicationThread caller,
+ String callingPackage, ComponentName component,
Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException {
if (!canAccessProfile(user.getIdentifier(), "Cannot show app details")) {
return;
}
+ final Intent intent;
long ident = Binder.clearCallingIdentity();
try {
String packageName = component.getPackageName();
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setSourceBounds(sourceBounds);
- mContext.startActivityAsUser(intent, opts, user);
} finally {
Binder.restoreCallingIdentity(ident);
}
+ mActivityManagerInternal.startActivityAsUser(caller, callingPackage,
+ intent, opts, user.getIdentifier());
}
/** Checks if user is a profile of or same as listeningUser.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4d8718f..ad0ca6e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8433,7 +8433,7 @@
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
- errorCode == PackageManager.INSTALL_FAILED_INVALID_APK) {
+ errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
@@ -22477,9 +22477,13 @@
}
final String volumeUuid = pkg.volumeUuid;
final String packageName = pkg.packageName;
- final ApplicationInfo app = (ps == null)
+
+ ApplicationInfo app = (ps == null)
? pkg.applicationInfo
: PackageParser.generateApplicationInfo(pkg, 0, ps.readUserState(userId), userId);
+ if (app == null) {
+ app = pkg.applicationInfo;
+ }
final int appId = UserHandle.getAppId(app.uid);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b3003ac..9af02a1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -234,6 +234,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayCutout;
+import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
@@ -7191,14 +7192,35 @@
}
@Override
- public boolean isDockSideAllowed(int dockSide) {
+ public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+ int displayHeight, int displayRotation) {
+ final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+ return isDockSideAllowed(dockSide, originalDockSide, barPosition, mNavigationBarCanMove);
+ }
- // We do not allow all dock sides at which the navigation bar touches the docked stack.
- if (!mNavigationBarCanMove) {
- return dockSide == DOCKED_TOP || dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT;
- } else {
- return dockSide == DOCKED_TOP || dockSide == DOCKED_LEFT;
+ @VisibleForTesting
+ static boolean isDockSideAllowed(int dockSide, int originalDockSide,
+ int navBarPosition, boolean navigationBarCanMove) {
+ if (dockSide == DOCKED_TOP) {
+ return true;
}
+
+ if (navigationBarCanMove) {
+ // Only allow the dockside opposite to the nav bar position in landscape
+ return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
+ || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
+ }
+
+ // Side is the same as original side
+ if (dockSide == originalDockSide) {
+ return true;
+ }
+
+ // Only if original docked side was top in portrait will allow left for landscape
+ if (dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP) {
+ return true;
+ }
+ return false;
}
void sendCloseSystemWindows() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ccbf502..8690a83 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1706,11 +1706,19 @@
DisplayCutout displayCutout, Rect outInsets);
/**
+ * @param displayRotation the current display rotation
+ * @param displayWidth the current display width
+ * @param displayHeight the current display height
+ * @param dockSide the dockside asking if allowed
+ * @param originalDockSide the side that was original docked to in split screen
* @return True if a specified {@param dockSide} is allowed on the current device, or false
* otherwise. It is guaranteed that at least one dock side for a particular orientation
* is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed.
+ * If navigation bar is movable then the docked side would bias towards the
+ * {@param originalDockSide}.
*/
- public boolean isDockSideAllowed(int dockSide);
+ public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+ int displayHeight, int displayRotation);
/**
* Called when the configuration has changed, and it's safe to load new values from resources.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 20283a7..a492672 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -719,13 +719,14 @@
* Plays the wireless charging sound for both wireless and non-wireless charging
*/
private void playChargingStartedSound() {
- // TODO (b/77912907): add back charging sound enabled check & default to charging sounds ON
+ final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
final boolean dndOff = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
== Settings.Global.ZEN_MODE_OFF;
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.CHARGING_STARTED_SOUND);
- if (dndOff && soundPath != null) {
+ if (enabled && dndOff && soundPath != null) {
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index c527533..cb84cf3 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -90,6 +90,16 @@
*/
private final Plugin[] mPlugins;
+ public static final int REASON_AUTOMATIC_ON = 0;
+ public static final int REASON_AUTOMATIC_OFF = 1;
+ public static final int REASON_MANUAL_ON = 2;
+ public static final int REASON_MANUAL_OFF = 3;
+ public static final int REASON_STICKY_RESTORE = 4;
+ public static final int REASON_INTERACTIVE_CHANGED = 5;
+ public static final int REASON_POLICY_CHANGED = 6;
+ public static final int REASON_PLUGGED_IN = 7;
+ public static final int REASON_SETTING_CHANGED = 8;
+
/**
* Plugin interface. All methods are guaranteed to be called on the same (handler) thread.
*/
@@ -113,7 +123,8 @@
return; // No need to send it if not enabled.
}
// Don't send the broadcast, because we never did so in this case.
- mHandler.postStateChanged(/*sendBroadcast=*/ false);
+ mHandler.postStateChanged(/*sendBroadcast=*/ false,
+ REASON_INTERACTIVE_CHANGED);
break;
case Intent.ACTION_BATTERY_CHANGED:
synchronized (mLock) {
@@ -184,7 +195,7 @@
if (!isEnabled()) {
return; // No need to send it if not enabled.
}
- mHandler.postStateChanged(/*sendBroadcast=*/ true);
+ mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED);
}
private class MyHandler extends Handler {
@@ -199,9 +210,9 @@
super(looper);
}
- public void postStateChanged(boolean sendBroadcast) {
+ public void postStateChanged(boolean sendBroadcast, int reason) {
obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
- ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
+ ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget();
}
public void postSystemReady() {
@@ -212,7 +223,9 @@
public void dispatchMessage(Message msg) {
switch (msg.what) {
case MSG_STATE_CHANGED:
- handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
+ handleBatterySaverStateChanged(
+ msg.arg1 == ARG_SEND_BROADCAST,
+ msg.arg2);
break;
case MSG_SYSTEM_READY:
@@ -227,14 +240,14 @@
/**
* Called by {@link PowerManagerService} to update the battery saver stete.
*/
- public void enableBatterySaver(boolean enable) {
+ public void enableBatterySaver(boolean enable, int reason) {
synchronized (mLock) {
if (mEnabled == enable) {
return;
}
mEnabled = enable;
- mHandler.postStateChanged(/*sendBroadcast=*/ true);
+ mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
}
}
@@ -275,7 +288,7 @@
* - When battery saver is on the interactive state changes.
* - When battery saver is on the battery saver policy changes.
*/
- void handleBatterySaverStateChanged(boolean sendBroadcast) {
+ void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
final LowPowerModeListener[] listeners;
final boolean enabled;
@@ -287,7 +300,8 @@
mPreviouslyEnabled ? 1 : 0, // Previously off or on.
mEnabled ? 1 : 0, // Now off or on.
isInteractive ? 1 : 0, // Device interactive state.
- mEnabled ? mBatterySaverPolicy.toEventLogString() : "");
+ mEnabled ? mBatterySaverPolicy.toEventLogString() : "",
+ reason);
mPreviouslyEnabled = mEnabled;
listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index 2860521..b9f31b1 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -18,6 +18,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -27,6 +28,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
import com.android.server.power.BatterySaverPolicy;
import com.android.server.power.BatterySaverStateMachineProto;
@@ -95,6 +97,18 @@
@GuardedBy("mLock")
private boolean mBatterySaverSnoozing;
+ /**
+ * Last reason passed to {@link #enableBatterySaverLocked}.
+ */
+ @GuardedBy("mLock")
+ private int mLastChangedIntReason;
+
+ /**
+ * Last reason passed to {@link #enableBatterySaverLocked}.
+ */
+ @GuardedBy("mLock")
+ private String mLastChangedStrReason;
+
private final ContentObserver mSettingsObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
@@ -149,11 +163,25 @@
});
}
+ /**
+ * Run a {@link Runnable} on a background handler.
+ */
@VisibleForTesting
void runOnBgThread(Runnable r) {
BackgroundThread.getHandler().post(r);
}
+ /**
+ * Run a {@link Runnable} on a background handler, but lazily. If the same {@link Runnable},
+ * it'll be first removed before a new one is posted.
+ */
+ @VisibleForTesting
+ void runOnBgThreadLazy(Runnable r, int delayMillis) {
+ final Handler h = BackgroundThread.getHandler();
+ h.removeCallbacks(r);
+ h.postDelayed(r, delayMillis);
+ }
+
void refreshSettingsLocked() {
final ContentResolver cr = mContext.getContentResolver();
@@ -199,14 +227,23 @@
mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky;
mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold;
+ if (thresholdChanged) {
+ // To avoid spamming the event log, we throttle logging here.
+ runOnBgThreadLazy(mThresholdChangeLogger, 2000);
+ }
+
if (enabledChanged) {
final String reason = batterySaverEnabled
? "Global.low_power changed to 1" : "Global.low_power changed to 0";
enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true,
- reason);
+ BatterySaverController.REASON_SETTING_CHANGED, reason);
}
}
+ private final Runnable mThresholdChangeLogger = () -> {
+ EventLogTags.writeBatterySaverSetting(mSettingBatterySaverTriggerThreshold);
+ };
+
/**
* {@link com.android.server.power.PowerManagerService} calls it when battery state changes.
*
@@ -257,18 +294,26 @@
}
if (mIsPowered) {
updateSnoozingLocked(false, "Plugged in");
- enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Plugged in");
+ enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+ BatterySaverController.REASON_PLUGGED_IN,
+ "Plugged in");
} else if (mSettingBatterySaverEnabledSticky) {
// Re-enable BS.
- enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, "Sticky restore");
+ enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
+ BatterySaverController.REASON_STICKY_RESTORE,
+ "Sticky restore");
} else if (mIsBatteryLevelLow) {
if (!mBatterySaverSnoozing && isAutoBatterySaverConfigured()) {
- enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, "Auto ON");
+ enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false,
+ BatterySaverController.REASON_AUTOMATIC_ON,
+ "Auto ON");
}
} else { // Battery not low
- enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Auto OFF");
+ enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+ BatterySaverController.REASON_AUTOMATIC_OFF,
+ "Auto OFF");
}
}
@@ -284,6 +329,8 @@
}
synchronized (mLock) {
enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true,
+ (enabled ? BatterySaverController.REASON_MANUAL_ON
+ : BatterySaverController.REASON_MANUAL_OFF),
(enabled ? "Manual ON" : "Manual OFF"));
}
}
@@ -292,10 +339,11 @@
* Actually enable / disable battery saver. Write the new state to the global settings
* and propagate it to {@link #mBatterySaverController}.
*/
- private void enableBatterySaverLocked(boolean enable, boolean manual, String reason) {
+ private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
+ String strReason) {
if (DEBUG) {
Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
- + " reason=" + reason);
+ + " reason=" + strReason + "(" + intReason + ")");
}
final boolean wasEnabled = mBatterySaverController.isEnabled();
@@ -309,6 +357,8 @@
if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
return;
}
+ mLastChangedIntReason = intReason;
+ mLastChangedStrReason = strReason;
if (manual) {
if (enable) {
@@ -330,12 +380,12 @@
mSettingBatterySaverEnabledSticky = enable;
putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
}
- mBatterySaverController.enableBatterySaver(enable);
+ mBatterySaverController.enableBatterySaver(enable, intReason);
if (DEBUG) {
Slog.d(TAG, "Battery saver: Enabled=" + enable
+ " manual=" + manual
- + " reason=" + reason);
+ + " reason=" + strReason + "(" + intReason + ")");
}
}
@@ -365,6 +415,11 @@
pw.print(" Enabled=");
pw.println(mBatterySaverController.isEnabled());
+ pw.print(" mLastChangedIntReason=");
+ pw.println(mLastChangedIntReason);
+ pw.print(" mLastChangedStrReason=");
+ pw.println(mLastChangedStrReason);
+
pw.print(" mBootCompleted=");
pw.println(mBootCompleted);
pw.print(" mSettingsLoaded=");
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 165a409..644e3c3 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -671,6 +671,17 @@
}
}
+ public void notifyAppStopping() {
+ synchronized(mWindowMap) {
+ if (mContainer == null) {
+ Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+ + mToken);
+ return;
+ }
+ mContainer.detachChildren();
+ }
+ }
+
public void notifyAppStopped() {
synchronized(mWindowMap) {
if (mContainer == null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a9560e6..966ca41 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -913,12 +913,16 @@
// try and clean up it's child surfaces. We need to prevent this from
// happening, so we sever the children, transfering their ownership
// from the client it-self to the parent surface (owned by us).
+ detachChildren();
+
+ mPendingRelaunchCount++;
+ }
+
+ void detachChildren() {
for (int i = mChildren.size() - 1; i >= 0; i--) {
final WindowState w = mChildren.get(i);
w.mWinAnimator.detachChildren();
}
-
- mPendingRelaunchCount++;
}
void finishRelaunching() {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 2cd2ef1..c8baced 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -170,7 +170,7 @@
final int orientation = mTmpRect2.width() <= mTmpRect2.height()
? ORIENTATION_PORTRAIT
: ORIENTATION_LANDSCAPE;
- final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation);
+ final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation);
final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
getContentWidth());
@@ -191,6 +191,39 @@
return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
}
+ /**
+ * Get the current docked side. Determined by its location of {@param bounds} within
+ * {@param displayRect} but if both are the same, it will try to dock to each side and determine
+ * if allowed in its respected {@param orientation}.
+ *
+ * @param bounds bounds of the docked task to get which side is docked
+ * @param displayRect bounds of the display that contains the docked task
+ * @param orientation the origination of device
+ * @return current docked side
+ */
+ int getDockSide(Rect bounds, Rect displayRect, int orientation) {
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // Portrait mode, docked either at the top or the bottom.
+ final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
+ if (diff > 0) {
+ return DOCKED_TOP;
+ } else if (diff < 0) {
+ return DOCKED_BOTTOM;
+ }
+ return canPrimaryStackDockTo(DOCKED_TOP) ? DOCKED_TOP : DOCKED_BOTTOM;
+ } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ // Landscape mode, docked either on the left or on the right.
+ final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
+ if (diff > 0) {
+ return DOCKED_LEFT;
+ } else if (diff < 0) {
+ return DOCKED_RIGHT;
+ }
+ return canPrimaryStackDockTo(DOCKED_LEFT) ? DOCKED_LEFT : DOCKED_RIGHT;
+ }
+ return DOCKED_INVALID;
+ }
+
void getHomeStackBoundsInDockedMode(Rect outBounds) {
final DisplayInfo di = mDisplayContent.getDisplayInfo();
mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
@@ -203,10 +236,20 @@
outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
di.logicalWidth, di.logicalHeight);
} else {
- // In landscape append the left position with the statusbar height to match the
+ // In landscape also inset the left/right side with the statusbar height to match the
// minimized size height in portrait mode.
- outBounds.set(mTaskHeightInMinimizedMode + dividerSize + mTmpRect.left + mTmpRect.top,
- 0, di.logicalWidth, di.logicalHeight);
+ final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top;
+ int left = mTmpRect.left;
+ int right = di.logicalWidth - mTmpRect.right;
+ if (stack != null) {
+ if (stack.getDockSide() == DOCKED_LEFT) {
+ left += primaryTaskWidth;
+ } else if (stack.getDockSide() == DOCKED_RIGHT) {
+ right -= primaryTaskWidth;
+ }
+ }
+ outBounds.set(left, 0, right, di.logicalHeight);
}
}
@@ -420,21 +463,9 @@
* @return true if the side provided is valid
*/
boolean canPrimaryStackDockTo(int dockSide) {
- if (mService.mPolicy.isDockSideAllowed(dockSide)) {
- // Side is the same as original side
- if (dockSide == mOriginalDockedSide) {
- return true;
- }
- // Special rule that the top in portrait is always valid
- if (dockSide == DOCKED_TOP) {
- return true;
- }
- // Only if original docked side was top in portrait will allow left side for landscape
- if (dockSide == DOCKED_LEFT && mOriginalDockedSide == DOCKED_TOP) {
- return true;
- }
- }
- return false;
+ final DisplayInfo di = mDisplayContent.getDisplayInfo();
+ return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide, di.logicalWidth,
+ di.logicalHeight, di.rotation);
}
void notifyDockedStackExistsChanged(boolean exists) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6b13edd..efc4e73 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -317,7 +317,7 @@
@VisibleForTesting
int getSnapshotMode(Task task) {
final AppWindowToken topChild = task.getTopChild();
- if (!task.isActivityTypeStandardOrUndefined()) {
+ if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
return SNAPSHOT_MODE_NONE;
} else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
return SNAPSHOT_MODE_APP_THEME;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 018765d..891ee2e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1462,27 +1462,7 @@
}
dc.getBounds(mTmpRect);
final int orientation = dc.getConfiguration().orientation;
- return getDockSideUnchecked(bounds, mTmpRect, orientation);
- }
-
- static int getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- // Portrait mode, docked either at the top or the bottom.
- if (bounds.top - displayRect.top <= displayRect.bottom - bounds.bottom) {
- return DOCKED_TOP;
- } else {
- return DOCKED_BOTTOM;
- }
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- // Landscape mode, docked either on the left or on the right.
- if (bounds.left - displayRect.left <= displayRect.right - bounds.right) {
- return DOCKED_LEFT;
- } else {
- return DOCKED_RIGHT;
- }
- } else {
- return DOCKED_INVALID;
- }
+ return dc.getDockedDividerController().getDockSide(bounds, mTmpRect, orientation);
}
boolean hasTaskForUser(int userId) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7b6ebf4..70afbc3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -156,6 +156,9 @@
// give any timezone code room without going into negative time.
private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;
+ private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
+ private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
+
/*
* Implementation class names. TODO: Move them to a codegen class or load
* them from the build system somehow.
@@ -396,6 +399,8 @@
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
Looper.prepareMainLooper();
+ Looper.getMainLooper().setSlowLogThresholdMs(
+ SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
// Initialize native services.
System.loadLibrary("android_servers");
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 9daea1a..1415ada 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -19,51 +19,44 @@
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.am.ActivityStackSupervisor
+ .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.ArgumentMatchers.any;
-
-import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.WaitResult;
-import android.content.ComponentName;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseIntArray;
-import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
-
+import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
/**
* Tests for the {@link ActivityStackSupervisor} class.
@@ -378,4 +371,28 @@
assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */));
}
+
+ /**
+ * Verify that split-screen primary stack will be chosen if activity is launched that targets
+ * split-screen secondary, but a matching existing instance is found on top of split-screen
+ * primary stack.
+ */
+ @Test
+ public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() throws Exception {
+ // Create primary split-screen stack with a task and an activity.
+ final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
+ .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+
+ // Find a launch stack for the top activity in split-screen primary, while requesting
+ // split-screen secondary.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
+
+ // Assert that the primary stack is returned.
+ assertEquals(primaryStack, result);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index e2ba4d5..213961c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -254,6 +254,12 @@
}
@Override
+ public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions) {
+ spiedContext.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions);
+ }
+
+ @Override
public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
spiedContext.sendBroadcast(intent, receiverPermission, options);
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
index 64637f4..30665b5 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
@@ -28,12 +28,19 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import android.graphics.PixelFormat;
import android.platform.test.annotations.Presubmit;
@@ -194,4 +201,53 @@
PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
}
+
+ @Test
+ public void testIsDockSideAllowedDockTop() throws Exception {
+ // Docked top is always allowed
+ assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+ true /* navigationBarCanMove */));
+ assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+ false /* navigationBarCanMove */));
+ }
+
+ @Test
+ public void testIsDockSideAllowedDockBottom() throws Exception {
+ // Cannot dock bottom
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM,
+ true /* navigationBarCanMove */));
+ }
+
+ @Test
+ public void testIsDockSideAllowedNavigationBarMovable() throws Exception {
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+ true /* navigationBarCanMove */));
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT,
+ true /* navigationBarCanMove */));
+ assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT,
+ true /* navigationBarCanMove */));
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+ true /* navigationBarCanMove */));
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT,
+ true /* navigationBarCanMove */));
+ assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT,
+ true /* navigationBarCanMove */));
+ }
+
+ @Test
+ public void testIsDockSideAllowedNavigationBarNotMovable() throws Exception {
+ // Navigation bar is not movable such as tablets
+ assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+ false /* navigationBarCanMove */));
+ assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM,
+ false /* navigationBarCanMove */));
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+ false /* navigationBarCanMove */));
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+ false /* navigationBarCanMove */));
+ assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM,
+ false /* navigationBarCanMove */));
+ assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+ false /* navigationBarCanMove */));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 1367f58..62fe6b2 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -144,6 +145,11 @@
void runOnBgThread(Runnable r) {
r.run();
}
+
+ @Override
+ void runOnBgThreadLazy(Runnable r, int delayMillis) {
+ r.run();
+ }
}
@Before
@@ -153,7 +159,7 @@
mMockBatterySaverController = mock(BatterySaverController.class);
doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
- .when(mMockBatterySaverController).enableBatterySaver(anyBoolean());
+ .when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt());
when(mMockBatterySaverController.isEnabled())
.thenAnswer((inv) -> mDevice.batterySaverEnabled);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1c2d538..013c672 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -552,7 +552,8 @@
}
@Override
- public boolean isDockSideAllowed(int dockSide) {
+ public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+ int displayHeight, int displayRotation) {
return false;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 37c646b..8ffec40 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -529,7 +530,7 @@
PKG, new ParceledListSlice(Arrays.asList(channel)));
final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -558,7 +559,7 @@
mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -602,7 +603,7 @@
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -760,7 +761,7 @@
@Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
@@ -774,7 +775,7 @@
@Test
public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
@@ -902,7 +903,7 @@
@Test
public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
@@ -910,14 +911,14 @@
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
- assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE);
+ assertEquals(0, notifs[0].getNotification().flags & FLAG_FOREGROUND_SERVICE);
}
@Test
public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags =
- Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE;
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
@@ -938,7 +939,7 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -961,7 +962,7 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -985,7 +986,7 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -2188,7 +2189,6 @@
@Test
public void testReadPolicyXml_readApprovedServicesFromXml() throws Exception {
final String upgradeXml = "<notification-policy version=\"1\">"
- + "<zen></zen>"
+ "<ranking></ranking>"
+ "<enabled_listeners>"
+ "<service_listing approved=\"test\" user=\"0\" primary=\"true\" />"
@@ -2216,7 +2216,6 @@
@Test
public void testReadPolicyXml_readApprovedServicesFromSettings() throws Exception {
final String preupgradeXml = "<notification-policy version=\"1\">"
- + "<zen></zen>"
+ "<ranking></ranking>"
+ "</notification-policy>";
mService.readPolicyXml(
@@ -2258,7 +2257,7 @@
NotificationChannel.DEFAULT_CHANNEL_ID)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
@@ -2273,7 +2272,7 @@
nb = new Notification.Builder(mContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
@@ -2692,6 +2691,26 @@
}
@Test
+ public void testVisualDifference_foreground() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setContentTitle("foo");
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .setContentTitle("bar");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
public void testVisualDifference_diffTitle() {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentTitle("foo");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index d846d21..36ec221 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -336,6 +336,90 @@
}
@Test
+ public void testRemoteViews_layoutChange() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(189);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
+ public void testRemoteViews_layoutSame() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(234);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
+ public void testRemoteViews_sequenceChange() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ when(a.getSequenceNumber()).thenReturn(1);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(234);
+ when(b.getSequenceNumber()).thenReturn(2);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
+ public void testRemoteViews_sequenceSame() {
+ RemoteViews a = mock(RemoteViews.class);
+ when(a.getLayoutId()).thenReturn(234);
+ when(a.getSequenceNumber()).thenReturn(1);
+ RemoteViews b = mock(RemoteViews.class);
+ when(b.getLayoutId()).thenReturn(234);
+ when(b.getSequenceNumber()).thenReturn(1);
+
+ Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+ Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+ n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
public void testActionsDifferent_null() {
Notification n1 = new Notification.Builder(mContext, "test")
.build();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 8183a74..8905950 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -1714,13 +1714,13 @@
}
@Test
- public void testAndroidPkgCanBypassDnd_creation() {
+ public void testAndroidPkgCannotBypassDnd_creation() {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
- assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+ assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
.canBypassDnd());
}
@@ -1745,7 +1745,7 @@
}
@Test
- public void testAndroidPkgCanBypassDnd_update() throws Exception {
+ public void testAndroidPkgCannotBypassDnd_update() throws Exception {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
@@ -1753,11 +1753,8 @@
update.setBypassDnd(true);
mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
- assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+ assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
.canBypassDnd());
-
- // setup + 1st check
- verify(mPm, times(2)).getPackageInfoAsUser(any(), anyInt(), anyInt());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index d02a983..afc1263 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,6 +17,9 @@
package com.android.server.notification;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertEquals;
@@ -569,8 +572,6 @@
mZenModeHelperSpy.mConfig.allowMessages = true;
mZenModeHelperSpy.mConfig.allowEvents = true;
mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
- mZenModeHelperSpy.mConfig.allowWhenScreenOff = true;
- mZenModeHelperSpy.mConfig.allowWhenScreenOn = true;
mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
@@ -593,8 +594,6 @@
mZenModeHelperSpy.mConfig.allowMessages = true;
mZenModeHelperSpy.mConfig.allowEvents = true;
mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
- mZenModeHelperSpy.mConfig.allowWhenScreenOff = true;
- mZenModeHelperSpy.mConfig.allowWhenScreenOn = true;
mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelperSpy.mConfig.manualRule.zenMode =
@@ -645,6 +644,115 @@
}
@Test
+ public void testMigrateSuppressedVisualEffects_oneExistsButOff() throws Exception {
+ String xml = "<zen version=\"6\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOff=\"false\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, false);
+
+ assertEquals(0, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
+
+ xml = "<zen version=\"6\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOn=\"false\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+
+ parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, false);
+
+ assertEquals(0, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
+ }
+
+ @Test
+ public void testMigrateSuppressedVisualEffects_bothExistButOff() throws Exception {
+ String xml = "<zen version=\"6\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOff=\"false\" visualScreenOn=\"false\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, false);
+
+ assertEquals(0, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
+ }
+
+ @Test
+ public void testMigrateSuppressedVisualEffects_bothExistButOn() throws Exception {
+ String xml = "<zen version=\"6\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOff=\"true\" visualScreenOn=\"true\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, false);
+
+ assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ | SUPPRESSED_EFFECT_LIGHTS
+ | SUPPRESSED_EFFECT_PEEK,
+ mZenModeHelperSpy.mConfig.suppressedVisualEffects);
+
+ xml = "<zen version=\"6\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOff=\"false\" visualScreenOn=\"true\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+
+ parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, false);
+
+ assertEquals(SUPPRESSED_EFFECT_PEEK, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
+
+ xml = "<zen version=\"6\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOff=\"true\" visualScreenOn=\"false\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+
+ parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, false);
+
+ assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS,
+ mZenModeHelperSpy.mConfig.suppressedVisualEffects);
+ }
+
+ @Test
public void testReadXmlResetDefaultRules() throws Exception {
setupZenConfig();
@@ -705,16 +813,6 @@
setupZenConfigMaintained();
}
- @Test
- public void testPolicyReadsSuppressedEffects() {
- mZenModeHelperSpy.mConfig.allowWhenScreenOff = true;
- mZenModeHelperSpy.mConfig.allowWhenScreenOn = true;
- mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
-
- NotificationManager.Policy policy = mZenModeHelperSpy.getNotificationPolicy();
- assertEquals(SUPPRESSED_EFFECT_BADGE, policy.suppressedVisualEffects);
- }
-
private void setupZenConfig() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
@@ -725,8 +823,6 @@
mZenModeHelperSpy.mConfig.allowMessages = true;
mZenModeHelperSpy.mConfig.allowEvents = true;
mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
- mZenModeHelperSpy.mConfig.allowWhenScreenOff = true;
- mZenModeHelperSpy.mConfig.allowWhenScreenOn = true;
mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelperSpy.mConfig.manualRule.zenMode =
@@ -746,8 +842,6 @@
assertTrue(mZenModeHelperSpy.mConfig.allowMessages);
assertTrue(mZenModeHelperSpy.mConfig.allowEvents);
assertTrue(mZenModeHelperSpy.mConfig.allowRepeatCallers);
- assertTrue(mZenModeHelperSpy.mConfig.allowWhenScreenOff);
- assertTrue(mZenModeHelperSpy.mConfig.allowWhenScreenOn);
assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
}
}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index f164bcd..e213d79 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2964,12 +2964,30 @@
* @hide
*/
public static final int OWNED_BY_DPC = 0;
+
/**
* Possible value for the OWNED_BY field.
* APN is owned by other sources.
* @hide
*/
public static final int OWNED_BY_OTHERS = 1;
+
+ /**
+ * The APN set id. When the user manually selects an APN or the framework sets an APN as
+ * preferred, all APNs with the same set id as the selected APN should be prioritized over
+ * APNs in other sets.
+ * @hide
+ */
+ public static final String APN_SET_ID = "apn_set_id";
+
+ /**
+ * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the
+ * user manually selects an APN with no set set, there is no need to prioritize any specific
+ * APN set ids.
+ * @hide
+ */
+ public static final int NO_SET_SET = 0;
+
}
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 88bed4e..8420165 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -453,7 +453,7 @@
*
* @param state call state
* @param phoneNumber call phone number. If application does not have
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission or carrier
+ * {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
* privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
* passed as an argument.
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 43ec716..512ffb1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -334,10 +334,12 @@
*
* <p>
* The {@link #EXTRA_STATE} extra indicates the new call state.
- * If the new state is RINGING, a second extra
- * {@link #EXTRA_INCOMING_NUMBER} provides the incoming phone number as
- * a String.
- *
+ * If a receiving app has {@link android.Manifest.permission#READ_CALL_LOG} permission, a second
+ * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outoing calls
+ * as a String. Note: If the receiving app has
+ * {@link android.Manifest.permission#READ_CALL_LOG} and
+ * {@link android.Manifest.permission#READ_PHONE_STATE} permission, it will receive the
+ * broadcast twice; one with the phone number and another without it.
* <p class="note">
* This was a {@link android.content.Context#sendStickyBroadcast sticky}
* broadcast in version 1.0, but it is no longer sticky.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index a182f2b..bbe38b7 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.telephony;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.Manifest;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -75,7 +78,7 @@
/**
* Check whether the app with the given pid/uid can read phone state.
*
- * <p>This method behaves in one of the following ways:
+ * <p>This method behaves in one of the following ways:
* <ul>
* <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
* READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
@@ -132,6 +135,40 @@
}
/**
+ * Check whether the app with the given pid/uid can read the call log.
+ * @return {@code true} if the specified app has the read call log permission and AppOpp granted
+ * to it, {@code false} otherwise.
+ */
+ public static boolean checkReadCallLog(
+ Context context, int subId, int pid, int uid, String callingPackage) {
+ return checkReadCallLog(
+ context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage);
+ }
+
+ @VisibleForTesting
+ public static boolean checkReadCallLog(
+ Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
+ String callingPackage) {
+
+ if (context.checkPermission(Manifest.permission.READ_CALL_LOG, pid, uid)
+ != PERMISSION_GRANTED) {
+ // If we don't have the runtime permission, but do have carrier privileges, that
+ // suffices for being able to see the call phone numbers.
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ enforceCarrierPrivilege(telephonySupplier, subId, uid, "readCallLog");
+ return true;
+ }
+ return false;
+ }
+
+ // We have READ_CALL_LOG permission, so return true as long as the AppOps bit hasn't been
+ // revoked.
+ AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ return appOps.noteOp(AppOpsManager.OP_READ_CALL_LOG, uid, callingPackage) ==
+ AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
* Returns whether the caller can read phone numbers.
*
* <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
@@ -204,7 +241,7 @@
public static void enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) ==
- PackageManager.PERMISSION_GRANTED) {
+ PERMISSION_GRANTED) {
return;
}
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 4dfd050..9d260eb 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -364,6 +364,13 @@
}
/** @hide */
+ @Override
+ public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
@SystemApi
@Override
public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 4ca175f..6ce66f0 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -98,7 +98,7 @@
private static final String LAUNCH_FILE = "applaunch.txt";
private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
private static final String DEFAULT_TRACE_CATEGORIES =
- "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm,binder_driver,hal";
+ "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm,binder_driver,hal,ss";
private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
index 2166240..25bd7c0 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
@@ -175,6 +175,12 @@
}
@Override
+ public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions) {
+ sendBroadcast(intent);
+ }
+
+ @Override
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
sendBroadcast(intent);
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index b333126..a7fffca 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -85,7 +85,7 @@
boolean disableNetwork(int netId, String packageName);
- void startScan(String packageName);
+ boolean startScan(String packageName);
List<ScanResult> getScanResults(String callingPackage);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9c6c8a9..a19965d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1635,8 +1635,7 @@
public boolean startScan(WorkSource workSource) {
try {
String packageName = mContext.getOpPackageName();
- mService.startScan(packageName);
- return true;
+ return mService.startScan(packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f3ffcad..20e49cd 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1070,4 +1070,16 @@
fail("setWifiApConfiguration should rethrow Exceptions from WifiService");
} catch (SecurityException e) { }
}
+
+ /**
+ * Check the call to startScan calls WifiService.
+ */
+ @Test
+ public void testStartScan() throws Exception {
+ when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(true);
+ assertTrue(mWifiManager.startScan());
+
+ when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(false);
+ assertFalse(mWifiManager.startScan());
+ }
}