Merge "Toast on broadcast receiver notification trampolines"
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
new file mode 100644
index 0000000..66d7348
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "ContentCapturePerfTests",
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ "collector-device-lib-platform",
+ "compatibility-device-util-axt",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/apct-tests/perftests/contentcapture/AndroidManifest.xml b/apct-tests/perftests/contentcapture/AndroidManifest.xml
new file mode 100644
index 0000000..ee5577f
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.contentcapture">
+
+ <uses-sdk android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.view.contentcapture.CustomTestActivity"
+ android:exported="true">
+ </activity>
+
+ <service
+ android:name="android.view.contentcapture.MyContentCaptureService"
+ android:label="PERF ContentCaptureService"
+ android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.contentcapture.ContentCaptureService" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.contentcapture" />
+</manifest>
diff --git a/apct-tests/perftests/contentcapture/AndroidTest.xml b/apct-tests/perftests/contentcapture/AndroidTest.xml
new file mode 100644
index 0000000..d2386bb
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs ContentCapturePerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ContentCapturePerfTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.contentcapture" />
+ </test>
+</configuration>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
new file mode 100644
index 0000000..ca1a11a
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
new file mode 100644
index 0000000..9bab32c
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <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" />
+
+ <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" />
+
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
new file mode 100644
index 0000000..f02c96d
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_CUSTOM_VIEWS;
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_LAYOUT_ID;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.app.Application;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.perftests.utils.PerfStatusReporter;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher;
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+/**
+ * Base class for all content capture tests.
+ */
+@LargeTest
+public abstract class AbstractContentCapturePerfTestCase {
+
+ private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName();
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+
+ private static int sOriginalStayOnWhilePluggedIn;
+ private static Context sContext = getInstrumentation().getTargetContext();
+
+ protected ActivitiesWatcher mActivitiesWatcher;
+
+ private MyContentCaptureService.ServiceWatcher mServiceWatcher;
+
+ @Rule
+ public ActivityTestRule<CustomTestActivity> mActivityRule =
+ new ActivityTestRule<>(CustomTestActivity.class, false, false);
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Rule
+ public TestRule mServiceDisablerRule = (base, description) -> {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ base.evaluate();
+ } finally {
+ Log.v(TAG, "@mServiceDisablerRule: safelyDisableService()");
+ safelyDisableService();
+ }
+ }
+ };
+ };
+
+ private void safelyDisableService() {
+ try {
+ resetService();
+ MyContentCaptureService.resetStaticState();
+
+ if (mServiceWatcher != null) {
+ mServiceWatcher.waitOnDestroy();
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "error disabling service", t);
+ }
+ }
+
+ /**
+ * Sets the content capture service.
+ */
+ private static void setService(@NonNull String service) {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "Setting service for user " + userId + " to " + service);
+ // TODO(b/123540602): use @TestingAPI to get max duration constant
+ runShellCommand("cmd content_capture set temporary-service %d %s 119000", userId, service);
+ }
+
+ /**
+ * Resets the content capture service.
+ */
+ private static void resetService() {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "Resetting back user " + userId + " to default service");
+ runShellCommand("cmd content_capture set temporary-service %d", userId);
+ }
+
+ private static int getCurrentUserId() {
+ return UserHandle.myUserId();
+ }
+
+ @BeforeClass
+ public static void setStayAwake() {
+ Log.v(TAG, "@BeforeClass: setStayAwake()");
+ // Some test cases will restart the activity, and stay awake is necessary to ensure that
+ // the test will not screen off during the test.
+ // Keeping the activity screen on is not enough, screen off may occur between the activity
+ // finished and the next start
+ final int stayOnWhilePluggedIn = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+ sOriginalStayOnWhilePluggedIn = -1;
+ if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
+ sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
+ // Keep the device awake during testing.
+ setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
+ }
+ }
+
+ @AfterClass
+ public static void resetStayAwake() {
+ Log.v(TAG, "@AfterClass: resetStayAwake()");
+ if (sOriginalStayOnWhilePluggedIn != -1) {
+ setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+ }
+ }
+
+ private static void setStayOnWhilePluggedIn(int value) {
+ runShellCommand(String.format("settings put global %s %d",
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+ }
+
+ @BeforeClass
+ public static void setAllowSelf() {
+ final ContentCaptureOptions options = new ContentCaptureOptions(null);
+ Log.v(TAG, "@BeforeClass: setAllowSelf(): options=" + options);
+ sContext.getApplicationContext().setContentCaptureOptions(options);
+ }
+
+ @AfterClass
+ public static void unsetAllowSelf() {
+ Log.v(TAG, "@AfterClass: unsetAllowSelf()");
+ clearOptions();
+ }
+
+ protected static void clearOptions() {
+ sContext.getApplicationContext().setContentCaptureOptions(null);
+ }
+
+ @BeforeClass
+ public static void disableDefaultService() {
+ Log.v(TAG, "@BeforeClass: disableDefaultService()");
+ setDefaultServiceEnabled(false);
+ }
+
+ @AfterClass
+ public static void enableDefaultService() {
+ Log.v(TAG, "@AfterClass: enableDefaultService()");
+ setDefaultServiceEnabled(true);
+ }
+
+ /**
+ * Enables / disables the default service.
+ */
+ private static void setDefaultServiceEnabled(boolean enabled) {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "setDefaultServiceEnabled(user=" + userId + ", enabled= " + enabled + ")");
+ runShellCommand("cmd content_capture set default-service-enabled %d %s", userId,
+ Boolean.toString(enabled));
+ }
+
+ @Before
+ public void prepareDevice() throws Exception {
+ Log.v(TAG, "@Before: prepareDevice()");
+
+ // Unlock screen.
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+ // Dismiss keyguard, in case it's set as "Swipe to unlock".
+ runShellCommand("wm dismiss-keyguard");
+
+ // Collapse notifications.
+ runShellCommand("cmd statusbar collapse");
+ }
+
+ @Before
+ public void registerLifecycleCallback() {
+ Log.v(TAG, "@Before: Registering lifecycle callback");
+ final Application app = (Application) sContext.getApplicationContext();
+ mActivitiesWatcher = new ActivitiesWatcher(GENERIC_TIMEOUT_MS);
+ app.registerActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+
+ @After
+ public void unregisterLifecycleCallback() {
+ Log.d(TAG, "@After: Unregistering lifecycle callback: " + mActivitiesWatcher);
+ if (mActivitiesWatcher != null) {
+ final Application app = (Application) sContext.getApplicationContext();
+ app.unregisterActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+ }
+
+ /**
+ * Sets {@link MyContentCaptureService} as the service for the current user and waits until
+ * its created, then add the perf test package into allow list.
+ */
+ public MyContentCaptureService enableService() throws InterruptedException {
+ if (mServiceWatcher != null) {
+ throw new IllegalStateException("There Can Be Only One!");
+ }
+
+ mServiceWatcher = MyContentCaptureService.setServiceWatcher();
+ setService(MyContentCaptureService.SERVICE_NAME);
+ mServiceWatcher.setAllowSelf();
+ return mServiceWatcher.waitOnCreate();
+ }
+
+ @NonNull
+ protected ActivityWatcher startWatcher() {
+ return mActivitiesWatcher.watch(CustomTestActivity.class);
+ }
+
+ /**
+ * Launch test activity with default login layout
+ */
+ protected CustomTestActivity launchActivity() {
+ return launchActivity(R.layout.test_login_activity, 0);
+ }
+
+ /**
+ * Launch test activity with give layout and parameter
+ */
+ protected CustomTestActivity launchActivity(int layoutId, int numViews) {
+ final Intent intent = new Intent(sContext, CustomTestActivity.class);
+ intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId);
+ intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews);
+ return mActivityRule.launchActivity(intent);
+ }
+
+ protected void finishActivity() {
+ try {
+ mActivityRule.finishActivity();
+ } catch (IllegalStateException e) {
+ // no op
+ }
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
new file mode 100644
index 0000000..e509837
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 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.contentcapture;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.perftests.contentcapture.R;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class CustomTestActivity extends Activity {
+ public static final String INTENT_EXTRA_LAYOUT_ID = "layout_id";
+ public static final String INTENT_EXTRA_CUSTOM_VIEWS = "custom_view_number";
+ public static final int MAX_VIEWS = 500;
+ private static final int CUSTOM_CONTAINER_LAYOUT_ID = R.layout.test_container_activity;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().hasExtra(INTENT_EXTRA_LAYOUT_ID)) {
+ final int layoutId = getIntent().getIntExtra(INTENT_EXTRA_LAYOUT_ID,
+ /* defaultValue= */0);
+ setContentView(layoutId);
+ if (layoutId == CUSTOM_CONTAINER_LAYOUT_ID) {
+ createCustomViews(findViewById(R.id.root_view),
+ getIntent().getIntExtra(INTENT_EXTRA_CUSTOM_VIEWS, MAX_VIEWS));
+ }
+ }
+ }
+
+ private void createCustomViews(LinearLayout root, int number) {
+ LinearLayout horizontalLayout = null;
+ for (int i = 0; i < number; i++) {
+ final int j = i % 8;
+ if (horizontalLayout != null && j == 0) {
+ root.addView(horizontalLayout);
+ horizontalLayout = null;
+ }
+ if (horizontalLayout == null) {
+ horizontalLayout = createHorizontalLayout();
+ }
+ horizontalLayout.addView(createItem(null, i));
+ }
+ if (horizontalLayout != null) {
+ root.addView(horizontalLayout);
+ }
+ }
+
+ private LinearLayout createHorizontalLayout() {
+ final LinearLayout layout = new LinearLayout(getApplicationContext());
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+ return layout;
+ }
+
+ private LinearLayout createItem(Drawable drawable, int index) {
+ final LinearLayout group = new LinearLayout(getApplicationContext());
+ group.setOrientation(LinearLayout.VERTICAL);
+ group.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f));
+
+ final TextView text = new TextView(this);
+ text.setText("i = " + index);
+ group.addView(text);
+
+ return group;
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
new file mode 100644
index 0000000..750d3b4
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.CREATED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+
+import android.perftests.utils.BenchmarkState;
+import android.view.View;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.Test;
+
+public class LoginTest extends AbstractContentCapturePerfTestCase {
+
+ @Test
+ public void testLaunchActivity() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_login_activity, 0);
+ }
+
+ @Test
+ public void testLaunchActivity_contain100Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 100);
+ }
+
+ @Test
+ public void testLaunchActivity_contain300Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 300);
+ }
+
+ @Test
+ public void testLaunchActivity_contain500Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 500);
+ }
+
+ @Test
+ public void testLaunchActivity_noService() throws Throwable {
+ testActivityLaunchTime(R.layout.test_login_activity, 0);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain100Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 100);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain300Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 300);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain500Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 500);
+ }
+
+ private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable {
+ final ActivityWatcher watcher = startWatcher();
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ launchActivity(layoutId, numViews);
+
+ // Ignore the time to finish the activity
+ state.pauseTiming();
+ watcher.waitFor(CREATED);
+ finishActivity();
+ watcher.waitFor(DESTROYED);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged() throws Throwable {
+ enableService();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_noService() throws Throwable {
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_noOptions() throws Throwable {
+ enableService();
+ clearOptions();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_notImportant() throws Throwable {
+ enableService();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+ username.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ private void testOnVisibilityAggregated(View view) throws Throwable {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ // Only count the time of onVisibilityAggregated()
+ state.pauseTiming();
+ mActivityRule.runOnUiThread(() -> {
+ state.resumeTiming();
+ view.onVisibilityAggregated(false);
+ state.pauseTiming();
+ });
+ mActivityRule.runOnUiThread(() -> {
+ state.resumeTiming();
+ view.onVisibilityAggregated(true);
+ state.pauseTiming();
+ });
+ state.resumeTiming();
+ }
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
new file mode 100644
index 0000000..b1dbb28
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.content.ComponentName;
+import android.service.contentcapture.ActivityEvent;
+import android.service.contentcapture.ContentCaptureService;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class MyContentCaptureService extends ContentCaptureService {
+
+ private static final String TAG = MyContentCaptureService.class.getSimpleName();
+ private static final String MY_PACKAGE = "com.android.perftests.contentcapture";
+ public static final String SERVICE_NAME = MY_PACKAGE + "/"
+ + MyContentCaptureService.class.getName();
+
+ private static ServiceWatcher sServiceWatcher;
+
+ @NonNull
+ public static ServiceWatcher setServiceWatcher() {
+ if (sServiceWatcher != null) {
+ throw new IllegalStateException("There Can Be Only One!");
+ }
+ sServiceWatcher = new ServiceWatcher();
+ return sServiceWatcher;
+ }
+
+ public static void resetStaticState() {
+ sServiceWatcher = null;
+ }
+
+ public static void clearServiceWatcher() {
+ if (sServiceWatcher != null) {
+ if (sServiceWatcher.mReadyToClear) {
+ sServiceWatcher.mService = null;
+ sServiceWatcher = null;
+ } else {
+ sServiceWatcher.mReadyToClear = true;
+ }
+ }
+ }
+
+ @Override
+ public void onConnected() {
+ Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher);
+
+ if (sServiceWatcher == null) {
+ Log.e(TAG, "onConnected() without a watcher");
+ return;
+ }
+
+ if (!sServiceWatcher.mReadyToClear && sServiceWatcher.mService != null) {
+ Log.e(TAG, "onConnected(): already created: " + sServiceWatcher);
+ return;
+ }
+
+ sServiceWatcher.mService = this;
+ sServiceWatcher.mCreated.countDown();
+ sServiceWatcher.mReadyToClear = false;
+ }
+
+ @Override
+ public void onDisconnected() {
+ Log.i(TAG, "onDisconnected: sServiceWatcher=" + sServiceWatcher);
+
+ if (sServiceWatcher == null) {
+ Log.e(TAG, "onDisconnected() without a watcher");
+ return;
+ }
+ if (sServiceWatcher.mService == null) {
+ Log.e(TAG, "onDisconnected(): no service on " + sServiceWatcher);
+ return;
+ }
+
+ sServiceWatcher.mDestroyed.countDown();
+ clearServiceWatcher();
+ }
+
+ @Override
+ public void onCreateContentCaptureSession(ContentCaptureContext context,
+ ContentCaptureSessionId sessionId) {
+ Log.i(TAG, "onCreateContentCaptureSession(ctx=" + context + ", session=" + sessionId);
+ }
+
+ @Override
+ public void onDestroyContentCaptureSession(ContentCaptureSessionId sessionId) {
+ Log.i(TAG, "onDestroyContentCaptureSession(session=" + sessionId + ")");
+ }
+
+ @Override
+ public void onContentCaptureEvent(ContentCaptureSessionId sessionId,
+ ContentCaptureEvent event) {
+ Log.i(TAG, "onContentCaptureEventsRequest(session=" + sessionId + "): " + event);
+ }
+
+ @Override
+ public void onActivityEvent(ActivityEvent event) {
+ Log.i(TAG, "onActivityEvent(): " + event);
+ }
+
+ public static final class ServiceWatcher {
+
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+ private final CountDownLatch mCreated = new CountDownLatch(1);
+ private final CountDownLatch mDestroyed = new CountDownLatch(1);
+ private boolean mReadyToClear = true;
+ private Pair<Set<String>, Set<ComponentName>> mAllowList;
+
+ private MyContentCaptureService mService;
+
+ @NonNull
+ public MyContentCaptureService waitOnCreate() throws InterruptedException {
+ await(mCreated, "not created");
+
+ if (mService == null) {
+ throw new IllegalStateException("not created");
+ }
+
+ if (mAllowList != null) {
+ Log.d(TAG, "Allow after created: " + mAllowList);
+ mService.setContentCaptureWhitelist(mAllowList.first, mAllowList.second);
+ }
+
+ return mService;
+ }
+
+ public void waitOnDestroy() throws InterruptedException {
+ await(mDestroyed, "not destroyed");
+ }
+
+ /**
+ * Allow just this package.
+ */
+ public void setAllowSelf() {
+ final ArraySet<String> pkgs = new ArraySet<>(1);
+ pkgs.add(MY_PACKAGE);
+ mAllowList = new Pair<>(pkgs, null);
+ }
+
+ @Override
+ public String toString() {
+ return "mService: " + mService + " created: " + (mCreated.getCount() == 0)
+ + " destroyed: " + (mDestroyed.getCount() == 0);
+ }
+
+ /**
+ * Awaits for a latch to be counted down.
+ */
+ private static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+ @Nullable Object... args)
+ throws InterruptedException {
+ final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (!called) {
+ throw new IllegalStateException(String.format(fmt, args)
+ + " in " + GENERIC_TIMEOUT_MS + "ms");
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
index abd39f8..860c134 100644
--- a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
+++ b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
@@ -16,24 +16,20 @@
package android.view;
-import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.PathParser;
import androidx.benchmark.BenchmarkState;
import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,6 +37,10 @@
@RunWith(AndroidJUnit4.class)
@LargeTest
public class CutoutSpecificationBenchmark {
+ private static final int DISPLAY_WIDTH = 1080;
+ private static final int DISPLAY_HEIGHT = 1920;
+ private static final float DISPLAY_DENSITY = 3.5f;
+
private static final String TAG = "CutoutSpecificationBenchmark";
private static final String BOTTOM_MARKER = "@bottom";
@@ -69,21 +69,6 @@
@Rule
public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
- private Context mContext;
- private DisplayMetrics mDisplayMetrics;
-
- /**
- * Setup the necessary member field used by test methods.
- */
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- mDisplayMetrics = new DisplayMetrics();
- mContext.getDisplay().getRealMetrics(mDisplayMetrics);
- }
-
-
private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
final RectF rectF = new RectF();
p.computeBounds(rectF, false /* unused */);
@@ -170,8 +155,8 @@
public void parseByOldMethodForDoubleCutout() {
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels,
- mDisplayMetrics.heightPixels, mDisplayMetrics.density);
+ oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_DENSITY);
}
}
@@ -179,8 +164,7 @@
public void parseByNewMethodForDoubleCutout() {
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
.parse(DOUBLE_CUTOUT_SPEC);
}
}
@@ -211,8 +195,8 @@
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+ .parse(spec);
}
}
@@ -233,8 +217,8 @@
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+ .parse(spec);
}
}
}
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 6475f57..18643ed 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -37,7 +37,7 @@
String reason);
// duration in milliseconds
- long getNotificationWhitelistDuration();
+ long getNotificationAllowlistDuration();
void setJobsActive(boolean active);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f26549c..b1bafee 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -890,7 +890,8 @@
"mms_temp_app_whitelist_duration";
private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION =
"sms_temp_app_whitelist_duration";
- private static final String KEY_NOTIFICATION_WHITELIST_DURATION =
+ // TODO(b/124466289): update value to match the name
+ private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION =
"notification_whitelist_duration";
/**
* Whether to wait for the user to unlock the device before causing screen-on to
@@ -1124,9 +1125,9 @@
* Amount of time we would like to whitelist an app that is handling a
* {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
- * @see #KEY_NOTIFICATION_WHITELIST_DURATION
+ * @see #KEY_NOTIFICATION_ALLOWLIST_DURATION
*/
- public long NOTIFICATION_WHITELIST_DURATION;
+ public long NOTIFICATION_ALLOWLIST_DURATION;
/**
* Pre idle time factor use to make idle delay longer
@@ -1230,8 +1231,8 @@
KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);
SMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
- NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
- KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
+ NOTIFICATION_ALLOWLIST_DURATION = mParser.getDurationMillis(
+ KEY_NOTIFICATION_ALLOWLIST_DURATION, 30 * 1000L);
WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, true);
PRE_IDLE_FACTOR_LONG = mParser.getFloat(KEY_PRE_IDLE_FACTOR_LONG, 1.67f);
PRE_IDLE_FACTOR_SHORT = mParser.getFloat(KEY_PRE_IDLE_FACTOR_SHORT, 0.33f);
@@ -1343,8 +1344,8 @@
TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw);
pw.println();
- pw.print(" "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("=");
- TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw);
+ pw.print(" "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION); pw.print("=");
+ TimeUtils.formatDuration(NOTIFICATION_ALLOWLIST_DURATION, pw);
pw.println();
pw.print(" "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
@@ -1792,8 +1793,8 @@
// duration in milliseconds
@Override
- public long getNotificationWhitelistDuration() {
- return mConstants.NOTIFICATION_WHITELIST_DURATION;
+ public long getNotificationAllowlistDuration() {
+ return mConstants.NOTIFICATION_ALLOWLIST_DURATION;
}
@Override
diff --git a/api/current.txt b/api/current.txt
index 7119902..1d2286b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24079,6 +24079,7 @@
field public static final int TYPE_IP = 20; // 0x14
field public static final int TYPE_LINE_ANALOG = 5; // 0x5
field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+ field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
field public static final int TYPE_TELEPHONY = 18; // 0x12
field public static final int TYPE_TV_TUNER = 17; // 0x11
field public static final int TYPE_UNKNOWN = 0; // 0x0
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 360e44f..c91c39a 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -5,6 +5,10 @@
field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
}
+ public class NotificationManager {
+ method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+ }
+
}
package android.content.rollback {
@@ -69,6 +73,10 @@
package android.os {
+ public class Binder implements android.os.IBinder {
+ method public final void markVintfStability();
+ }
+
public class StatsFrameworkInitializer {
method public static void registerServiceWrappers();
method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
diff --git a/api/removed.txt b/api/removed.txt
index 5a24f62..58dbeb8 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/api/system-current.txt b/api/system-current.txt
index fa45430..11db781 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4172,10 +4172,6 @@
field public static final int ROLE_OUTPUT = 2; // 0x2
}
- public final class AudioDeviceInfo {
- field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
- }
-
public final class AudioFocusInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAttributes();
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 881cfa3..e43c7d4 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -417,9 +417,6 @@
android.app.IProcessObserver$Stub$Proxy
android.app.IProcessObserver$Stub
android.app.IProcessObserver
-android.app.IRequestFinishCallback$Stub$Proxy
-android.app.IRequestFinishCallback$Stub
-android.app.IRequestFinishCallback
android.app.ISearchManager$Stub$Proxy
android.app.ISearchManager$Stub
android.app.ISearchManager
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 107fb263..4a982dd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -150,7 +150,6 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -3798,22 +3797,6 @@
return false;
}
- private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
- private final WeakReference<Activity> mActivityRef;
-
- RequestFinishCallback(WeakReference<Activity> activityRef) {
- mActivityRef = activityRef;
- }
-
- @Override
- public void requestFinish() {
- Activity activity = mActivityRef.get();
- if (activity != null) {
- activity.mHandler.post(activity::finishAfterTransition);
- }
- }
- }
-
/**
* Called when the activity has detected the user's press of the back
* key. The default implementation simply finishes the current activity,
@@ -3837,9 +3820,8 @@
try {
// Inform activity task manager that the activity received a back press
// while at the root of the task. This call allows ActivityTaskManager
- // to intercept or defer finishing.
- ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken,
- new RequestFinishCallback(new WeakReference<>(this)));
+ // to intercept or move the task to the back.
+ ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken);
} catch (RemoteException e) {
finishAfterTransition();
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7a6f558..a88c6a8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1019,12 +1019,6 @@
return ActivityTaskManager.getMaxRecentTasksStatic();
}
- /** @removed */
- @Deprecated
- public static int getMaxNumPictureInPictureActions() {
- return 3;
- }
-
/**
* Information you can set and retrieve about the current activity within the recent task list.
*/
@@ -3739,7 +3733,8 @@
* manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
* </p>
*
- * @param state The state data
+ * @param state The state data. To be advised, <b>DO NOT</b> include sensitive information/data
+ * (PII, SPII, or other sensitive user data) here. Maximum length is 128 bytes.
*/
public void setProcessStateSummary(@Nullable byte[] state) {
try {
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index bac432e4..15237be 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -48,17 +48,18 @@
ClassLoader parent, String classLoaderName) {
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName,
- null);
+ null, null);
}
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
- libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
+ libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
/**
@@ -77,14 +78,22 @@
return loader;
}
+ // TODO(b/142191088): allow (Java) shared libraries to have <uses-native-library>
+ // Until that is supported, assume that all native shared libraries are used.
+ // "ALL" is a magic string that libnativeloader uses to unconditionally add all available
+ // native shared libraries to the classloader.
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ nativeSharedLibraries.add("ALL");
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
- librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries);
+ librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
- String classLoaderName, List<ClassLoader> sharedLibraries) {
+ String classLoaderName, List<ClassLoader> sharedLibraries,
+ List<String> nativeSharedLibraries) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -113,7 +122,8 @@
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
- targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
+ targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -185,7 +195,8 @@
// assume cached libraries work with current sdk since they are built-in
ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
- null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/);
+ null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
+ null /* nativeSharedLibraries */);
if (classLoader == null) {
// bad configuration or break in classloading code
@@ -255,7 +266,8 @@
// The cache key is passed separately to enable the stub WebView to be cached under the
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
- cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
+ cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
+ null /* nativeSharedLibraries */);
}
/**
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index f428b48..72a3637d 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -26,7 +26,6 @@
import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
-import android.app.IRequestFinishCallback;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.ITaskStackListener;
@@ -458,9 +457,7 @@
/**
* Reports that an Activity received a back key press when there were no additional activities
- * on the back stack. If the Activity should be finished, the callback will be invoked. A
- * callback is used instead of finishing the activity directly from the server such that the
- * client may perform actions prior to finishing.
+ * on the back stack.
*/
- void onBackPressedOnTaskRoot(in IBinder activityToken, in IRequestFinishCallback callback);
+ void onBackPressedOnTaskRoot(in IBinder activityToken);
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0deef53..a970322 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -185,6 +185,7 @@
List<ComponentName> getEnabledNotificationListeners(int userId);
ComponentName getAllowedNotificationAssistantForUser(int userId);
ComponentName getAllowedNotificationAssistant();
+ boolean hasEnabledNotificationListener(String packageName, int userId);
@UnsupportedAppUsage
int getZenMode();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f9b48e7..aa6a08b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -412,6 +412,12 @@
return;
}
for (SharedLibraryInfo lib : sharedLibraries) {
+ if (lib.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
List<String> paths = lib.getAllCodePaths();
outSeenPaths.addAll(paths);
for (String path : paths) {
@@ -696,6 +702,12 @@
}
List<ClassLoader> loaders = new ArrayList<>();
for (SharedLibraryInfo info : sharedLibraries) {
+ if (info.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
loaders.add(createSharedLibraryLoader(
info, isBundledApp, librarySearchPath, libraryPermittedPath));
}
@@ -802,12 +814,9 @@
makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
- String libraryPermittedPath = mDataDir;
- if (mActivityThread == null) {
- // In a zygote context where mActivityThread is null we can't access the app data dir
- // and including this in libraryPermittedPath would cause SELinux denials.
- libraryPermittedPath = "";
- }
+ // Including an inaccessible dir in libraryPermittedPath would cause SELinux denials
+ // when the loader attempts to canonicalise the path. so we don't.
+ String libraryPermittedPath = canAccessDataDir() ? mDataDir : "";
if (isBundledApp) {
// For bundled apps, add the base directory of the app (e.g.,
@@ -898,10 +907,19 @@
mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
libraryPermittedPath);
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ if (mApplicationInfo.sharedLibraryInfos != null) {
+ for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
+ if (info.isNative()) {
+ nativeSharedLibraries.add(info.getName());
+ }
+ }
+ }
+
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
- mApplicationInfo.classLoaderName, sharedLibraries);
+ mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
setThreadPolicy(oldPolicy);
@@ -951,6 +969,33 @@
}
}
+ /**
+ * Return whether we can access the package's private data directory in order to be able to
+ * load code from it.
+ */
+ private boolean canAccessDataDir() {
+ // In a zygote context where mActivityThread is null we can't access the app data dir.
+ if (mActivityThread == null) {
+ return false;
+ }
+
+ // A package can access its own data directory (the common case, so short-circuit it).
+ if (Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
+ return true;
+ }
+
+ // Temporarily disable logging of disk reads on the Looper thread as this is necessary -
+ // and the loader will access the directory anyway if we don't check it.
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
+ try {
+ // We are constructing a classloader for a different package. It is likely,
+ // but not certain, that we can't acccess its app data dir - so check.
+ return new File(mDataDir).canExecute();
+ } finally {
+ setThreadPolicy(oldPolicy);
+ }
+ }
+
@UnsupportedAppUsage
public ClassLoader getClassLoader() {
synchronized (this) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f82ab7b..68e6561 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -958,7 +958,7 @@
*
* @hide
*/
- private IBinder mWhitelistToken;
+ private IBinder mAllowlistToken;
/**
* Must be set by a process to start associating tokens with Notification objects
@@ -966,7 +966,7 @@
*
* @hide
*/
- static public IBinder processWhitelistToken;
+ static public IBinder processAllowlistToken;
/**
* {@link #extras} key: this is the title of the notification,
@@ -2245,12 +2245,12 @@
{
int version = parcel.readInt();
- mWhitelistToken = parcel.readStrongBinder();
- if (mWhitelistToken == null) {
- mWhitelistToken = processWhitelistToken;
+ mAllowlistToken = parcel.readStrongBinder();
+ if (mAllowlistToken == null) {
+ mAllowlistToken = processAllowlistToken;
}
// Propagate this token to all pending intents that are unmarshalled from the parcel.
- parcel.setClassCookie(PendingIntent.class, mWhitelistToken);
+ parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
when = parcel.readLong();
creationTime = parcel.readLong();
@@ -2368,7 +2368,7 @@
* @hide
*/
public void cloneInto(Notification that, boolean heavy) {
- that.mWhitelistToken = this.mWhitelistToken;
+ that.mAllowlistToken = this.mAllowlistToken;
that.when = this.when;
that.creationTime = this.creationTime;
that.mSmallIcon = this.mSmallIcon;
@@ -2678,7 +2678,7 @@
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
- parcel.writeStrongBinder(mWhitelistToken);
+ parcel.writeStrongBinder(mAllowlistToken);
parcel.writeLong(when);
parcel.writeLong(creationTime);
if (mSmallIcon == null && icon != 0) {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index eef9c02..adbe6bb 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -801,8 +802,8 @@
*
* <p>The name and description should only be changed if the locale changes
* or in response to the user renaming this channel. For example, if a user has a channel
- * named 'John Doe' that represents messages from a 'John Doe', and 'John Doe' changes his name
- * to 'John Smith,' the channel can be renamed to match.
+ * named 'Messages' and the user changes their locale, this channel's name should be updated
+ * with the translation of 'Messages' in the new locale.
*
* <p>The importance of an existing channel will only be changed if the new importance is lower
* than the current value and the user has not altered any settings on this channel.
@@ -1539,6 +1540,25 @@
}
}
+ /**
+ * Whether the given user has an enabled
+ * {@link android.service.notification.NotificationListenerService} with the given package name.
+ *
+ * @param packageName the package name of the NotificationListenerService class
+ * @param userHandle the handle of the user that set the listener
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("UserHandle")
+ public boolean hasEnabledNotificationListener(@NonNull String packageName,
+ @NonNull UserHandle userHandle) {
+ INotificationManager service = getService();
+ try {
+ return service.hasEnabledNotificationListener(packageName, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
private Context mContext;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5f6befd..e990fd7 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -525,6 +525,9 @@
if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
protLevel += "|appPredictor";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
+ protLevel += "|companion";
+ }
if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
protLevel += "|retailDemo";
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index da2a3d8..8625637 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -79,6 +79,7 @@
private final long mVersion;
private final @Type int mType;
+ private final boolean mIsNative;
private final VersionedPackage mDeclaringPackage;
private final List<VersionedPackage> mDependentPackages;
private List<SharedLibraryInfo> mDependencies;
@@ -93,13 +94,14 @@
* @param type The lib type.
* @param declaringPackage The package that declares the library.
* @param dependentPackages The packages that depend on the library.
+ * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
*
* @hide
*/
public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
String name, long version, int type,
VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
- List<SharedLibraryInfo> dependencies) {
+ List<SharedLibraryInfo> dependencies, boolean isNative) {
mPath = path;
mPackageName = packageName;
mCodePaths = codePaths;
@@ -109,6 +111,16 @@
mDeclaringPackage = declaringPackage;
mDependentPackages = dependentPackages;
mDependencies = dependencies;
+ mIsNative = isNative;
+ }
+
+ /** @hide */
+ public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+ String name, long version, int type,
+ VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+ List<SharedLibraryInfo> dependencies) {
+ this(path, packageName, codePaths, name, version, type, declaringPackage, dependentPackages,
+ dependencies, false /* isNative */);
}
private SharedLibraryInfo(Parcel parcel) {
@@ -125,6 +137,7 @@
mDeclaringPackage = parcel.readParcelable(null);
mDependentPackages = parcel.readArrayList(null);
mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
+ mIsNative = parcel.readBoolean();
}
/**
@@ -137,6 +150,15 @@
}
/**
+ * Tells whether this library is a native shared library or not.
+ *
+ * @hide
+ */
+ public boolean isNative() {
+ return mIsNative;
+ }
+
+ /**
* Gets the library name an app defines in its manifest
* to depend on the library.
*
@@ -320,6 +342,7 @@
parcel.writeParcelable(mDeclaringPackage, flags);
parcel.writeList(mDependentPackages);
parcel.writeTypedList(mDependencies);
+ parcel.writeBoolean(mIsNative);
}
private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 2ee0ad6..872098c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -92,6 +92,10 @@
ParsingPackage addUsesOptionalLibrary(String libraryName);
+ ParsingPackage addUsesNativeLibrary(String libraryName);
+
+ ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage addUsesStaticLibrary(String libraryName);
ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
@@ -219,6 +223,8 @@
ParsingPackage removeUsesOptionalLibrary(String libraryName);
+ ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage setAnyDensity(int anyDensity);
ParsingPackage setAppComponentFactory(String appComponentFactory);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f932bc2..0c0dc31 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -186,6 +186,13 @@
@NonNull
@DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesNativeLibraries = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesOptionalNativeLibraries = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
private List<String> usesStaticLibraries = emptyList();
@Nullable
private long[] usesStaticLibrariesVersions;
@@ -669,6 +676,27 @@
}
@Override
+ public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
+ this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+
+ @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
+ libraryName);
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
TextUtils.safeIntern(libraryName));
@@ -982,6 +1010,8 @@
sForInternedStringList.parcel(this.libraryNames, dest, flags);
sForInternedStringList.parcel(this.usesLibraries, dest, flags);
sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
dest.writeLongArray(this.usesStaticLibrariesVersions);
@@ -1144,6 +1174,8 @@
this.libraryNames = sForInternedStringList.unparcel(in);
this.usesLibraries = sForInternedStringList.unparcel(in);
this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
+ this.usesNativeLibraries = sForInternedStringList.unparcel(in);
+ this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibrariesVersions = in.createLongArray();
@@ -1417,6 +1449,18 @@
@NonNull
@Override
+ public List<String> getUsesNativeLibraries() {
+ return usesNativeLibraries;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesOptionalNativeLibraries() {
+ return usesOptionalNativeLibraries;
+ }
+
+ @NonNull
+ @Override
public List<String> getUsesStaticLibraries() {
return usesStaticLibraries;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 5b53c18..7e0fe7d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -230,6 +230,19 @@
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 3688f1b..e1f08f3 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -701,6 +701,8 @@
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "uses-package":
// Dependencies for app installers; we don't currently try to
// enforce this.
@@ -2017,6 +2019,8 @@
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "processes":
return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
case "uses-package":
@@ -2178,6 +2182,37 @@
}
@NonNull
+ private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesNativeLibrary_name);
+ boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
+ true);
+
+ if (lname != null) {
+ if (req) {
+ // Upgrade to treat as stronger constraint
+ pkg.addUsesNativeLibrary(lname)
+ .removeUsesOptionalNativeLibrary(lname);
+ } else {
+ // Ignore if someone already defined as required
+ if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
+ pkg.addUsesOptionalNativeLibrary(lname);
+ }
+ }
+ }
+
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
throws IOException, XmlPullParserException {
diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
index c708d20..a16e878 100644
--- a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
@@ -22,7 +22,7 @@
*
* @hide
*/
-interface IHdmiVendorCommandListener {
+oneway interface IHdmiVendorCommandListener {
void onReceived(int logicalAddress, int destAddress, in byte[] operands, boolean hasVendorId);
void onControlStateChanged(boolean enabled, int reason);
}
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 5496e17..d8a2082 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -39,7 +39,7 @@
/**
* Min required eventlog line length.
* See: android/util/cts/EventLogTest.java
- * Size checks enforced here are intended only as sanity checks;
+ * Size limits enforced here are intended only as a precaution;
* your logs may be truncated earlier. Please log responsibly.
*
* @hide
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a0207c8..515704c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -543,15 +543,17 @@
/**
* Mark as being built with VINTF-level stability promise. This API should
- * only ever be invoked by the build system. It means that the interface
- * represented by this binder is guaranteed to be kept stable for several
- * years, and the build system also keeps snapshots of these APIs and
- * invokes the AIDL compiler to make sure that these snapshots are
- * backwards compatible. Instead of using this API, use an @VintfStability
- * interface.
+ * only ever be invoked by generated code from the aidl compiler. It means
+ * that the interface represented by this binder is guaranteed to be kept
+ * stable for several years, according to the VINTF compatibility lifecycle,
+ * and the build system also keeps snapshots of these APIs and invokes the
+ * AIDL compiler to make sure that these snapshots are backwards compatible.
+ * Instead of using this API, use the @VintfStability annotation on your
+ * AIDL interface.
*
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final native void markVintfStability();
/**
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 9b360ed..bedbba0 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -99,6 +99,35 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ContentsFlags {}
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PARCELABLE_STABILITY_" }, value = {
+ PARCELABLE_STABILITY_LOCAL,
+ PARCELABLE_STABILITY_VINTF,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Stability {}
+
+ /**
+ * Something that is not meant to cross compilation boundaries.
+ *
+ * Note: unlike binder/Stability.h which uses bitsets to detect stability,
+ * since we don't currently have a notion of different local locations,
+ * higher stability levels are formed at higher levels.
+ *
+ * For instance, contained entirely within system partitions.
+ * @see #getStability()
+ * @see ParcelableHolder
+ * @hide
+ */
+ public static final int PARCELABLE_STABILITY_LOCAL = 0x0000;
+ /**
+ * Something that is meant to be used between system and vendor.
+ * @see #getStability()
+ * @see ParcelableHolder
+ * @hide
+ */
+ public static final int PARCELABLE_STABILITY_VINTF = 0x0001;
+
/**
* Descriptor bit used with {@link #describeContents()}: indicates that
* the Parcelable object's flattened representation includes a file descriptor.
@@ -129,8 +158,8 @@
* @return true if this parcelable is stable.
* @hide
*/
- default boolean isStable() {
- return false;
+ default @Stability int getStability() {
+ return PARCELABLE_STABILITY_LOCAL;
}
/**
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
index c37a2ff..181f94b 100644
--- a/core/java/android/os/ParcelableHolder.java
+++ b/core/java/android/os/ParcelableHolder.java
@@ -37,10 +37,10 @@
* if {@link ParcelableHolder} contains value, otherwise, both are null.
*/
private Parcel mParcel;
- private boolean mIsStable = false;
+ private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL;
- public ParcelableHolder(boolean isStable) {
- mIsStable = isStable;
+ public ParcelableHolder(@Parcelable.Stability int stability) {
+ mStability = stability;
}
private ParcelableHolder() {
@@ -50,11 +50,11 @@
/**
* {@link ParcelableHolder}'s stability is determined by the parcelable
* which contains this ParcelableHolder.
- * For more detail refer to {@link Parcelable#isStable}.
+ * For more detail refer to {@link Parcelable#getStability}.
*/
@Override
- public boolean isStable() {
- return mIsStable;
+ public @Parcelable.Stability int getStability() {
+ return mStability;
}
@NonNull
@@ -81,7 +81,8 @@
* @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
*/
public synchronized boolean setParcelable(@Nullable Parcelable p) {
- if (p != null && this.isStable() && !p.isStable()) {
+ // a ParcelableHolder can only hold things at its stability or higher
+ if (p != null && this.getStability() > p.getStability()) {
return false;
}
mParcelable = p;
@@ -123,7 +124,7 @@
* Read ParcelableHolder from a parcel.
*/
public synchronized void readFromParcel(@NonNull Parcel parcel) {
- this.mIsStable = parcel.readBoolean();
+ this.mStability = parcel.readInt();
mParcelable = null;
@@ -145,7 +146,7 @@
@Override
public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeBoolean(this.mIsStable);
+ parcel.writeInt(this.mStability);
if (mParcel != null) {
parcel.writeInt(mParcel.dataSize());
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index f4aa354..8a7cf60 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -310,6 +310,7 @@
@Nullable VibrationEffect effect) {
mAudioAttributes = audio;
setUsage(audio);
+ setFlags(audio);
applyHapticFeedbackHeuristics(effect);
}
@@ -366,6 +367,12 @@
}
}
+ private void setFlags(@NonNull AudioAttributes audio) {
+ if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
+ mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY;
+ }
+ }
+
/**
* Combines all of the attributes that have been set and returns a new
* {@link VibrationAttributes} object.
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index e3cb382..ab19fa9 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -551,7 +551,7 @@
out.append(((ImageSpan) style[j]).getSource());
out.append("\">");
- // Don't output the dummy character underlying the image.
+ // Don't output the placeholder character underlying the image.
i = next;
}
if (style[j] instanceof AbsoluteSizeSpan) {
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 4fe6752..38e3b39 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -26,8 +26,6 @@
import android.text.Spanned;
import android.text.SpannedString;
-import libcore.icu.LocaleData;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@@ -287,8 +285,10 @@
*/
@UnsupportedAppUsage
public static String getTimeFormatString(Context context, int userHandle) {
- final LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
- return is24HourFormat(context, userHandle) ? d.timeFormat_Hm : d.timeFormat_hm;
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ context.getResources().getConfiguration().locale);
+ return is24HourFormat(context, userHandle) ? dtpg.getBestPattern("Hm")
+ : dtpg.getBestPattern("hm");
}
/**
@@ -475,7 +475,6 @@
SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
int count;
- LocaleData localeData = LocaleData.get(Locale.getDefault());
DateFormatSymbols dfs = getIcuDateFormatSymbols(Locale.getDefault());
String[] amPm = dfs.getAmPmStrings();
@@ -506,7 +505,7 @@
break;
case 'c':
case 'E':
- replacement = getDayOfWeekString(localeData,
+ replacement = getDayOfWeekString(dfs,
inDate.get(Calendar.DAY_OF_WEEK), count, c);
break;
case 'K': // hour in am/pm (0-11)
@@ -534,8 +533,7 @@
break;
case 'L':
case 'M':
- replacement = getMonthString(localeData,
- inDate.get(Calendar.MONTH), count, c);
+ replacement = getMonthString(dfs, inDate.get(Calendar.MONTH), count, c);
break;
case 'm':
replacement = zeroPad(inDate.get(Calendar.MINUTE), count);
@@ -568,25 +566,29 @@
}
}
- private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) {
+ private static String getDayOfWeekString(DateFormatSymbols dfs, int day, int count, int kind) {
boolean standalone = (kind == 'c');
+ int context = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
+ final int width;
if (count == 5) {
- return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day];
+ width = DateFormatSymbols.NARROW;
} else if (count == 4) {
- return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day];
+ width = DateFormatSymbols.WIDE;
} else {
- return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day];
+ width = DateFormatSymbols.ABBREVIATED;
}
+ return dfs.getWeekdays(context, width)[day];
}
- private static String getMonthString(LocaleData ld, int month, int count, int kind) {
+ private static String getMonthString(DateFormatSymbols dfs, int month, int count, int kind) {
boolean standalone = (kind == 'L');
+ int monthContext = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
if (count == 5) {
- return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.NARROW)[month];
} else if (count == 4) {
- return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.WIDE)[month];
} else if (count == 3) {
- return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.ABBREVIATED)[month];
} else {
// Calendar.JANUARY == 0, so add 1 to month.
return zeroPad(month+1, count);
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 741312f..ff08269 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.icu.text.DateFormatSymbols;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
@@ -27,8 +28,6 @@
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -203,17 +202,23 @@
*/
@Deprecated
public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
- LocaleData d = LocaleData.get(Locale.getDefault());
- String[] names;
+ DateFormatSymbols dfs = DateFormatSymbols.getInstance();
+ final int width;
switch (abbrev) {
- case LENGTH_LONG: names = d.longWeekdayNames; break;
- case LENGTH_MEDIUM: names = d.shortWeekdayNames; break;
- case LENGTH_SHORT: names = d.shortWeekdayNames; break; // TODO
- case LENGTH_SHORTER: names = d.shortWeekdayNames; break; // TODO
- case LENGTH_SHORTEST: names = d.tinyWeekdayNames; break;
- default: names = d.shortWeekdayNames; break;
+ case LENGTH_LONG:
+ width = DateFormatSymbols.WIDE;
+ break;
+ case LENGTH_SHORTEST:
+ width = DateFormatSymbols.NARROW;
+ break;
+ case LENGTH_MEDIUM:
+ case LENGTH_SHORT: // TODO
+ case LENGTH_SHORTER: // TODO
+ default:
+ width = DateFormatSymbols.ABBREVIATED;
+ break;
}
- return names[dayOfWeek];
+ return dfs.getWeekdays(DateFormatSymbols.FORMAT, width)[dayOfWeek];
}
/**
@@ -242,17 +247,23 @@
*/
@Deprecated
public static String getMonthString(int month, int abbrev) {
- LocaleData d = LocaleData.get(Locale.getDefault());
- String[] names;
+ DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.getDefault());
+ final int width;
switch (abbrev) {
- case LENGTH_LONG: names = d.longMonthNames; break;
- case LENGTH_MEDIUM: names = d.shortMonthNames; break;
- case LENGTH_SHORT: names = d.shortMonthNames; break;
- case LENGTH_SHORTER: names = d.shortMonthNames; break;
- case LENGTH_SHORTEST: names = d.tinyMonthNames; break;
- default: names = d.shortMonthNames; break;
+ case LENGTH_LONG:
+ width = DateFormatSymbols.WIDE;
+ break;
+ case LENGTH_SHORTEST:
+ width = DateFormatSymbols.NARROW;
+ break;
+ case LENGTH_MEDIUM:
+ case LENGTH_SHORT:
+ case LENGTH_SHORTER:
+ default:
+ width = DateFormatSymbols.ABBREVIATED;
+ break;
}
- return names[month];
+ return dfs.getMonths(DateFormatSymbols.FORMAT, width)[month];
}
/**
diff --git a/core/java/android/text/format/OWNERS b/core/java/android/text/format/OWNERS
new file mode 100644
index 0000000..32adc12
--- /dev/null
+++ b/core/java/android/text/format/OWNERS
@@ -0,0 +1,3 @@
+# Inherits OWNERS from parent directory, plus the following
+
+vichang@google.com
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 80c8ec8..5b5c854 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -361,7 +361,7 @@
*/
@Override
public String toString() {
- // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+ // toString() uses its own TimeCalculator rather than the shared one. Otherwise weird stuff
// happens during debugging when the debugger calls toString().
TimeCalculator calculator = new TimeCalculator(this.timezone);
calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index 9393f36..c71dfbb 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -22,11 +22,10 @@
import android.content.res.Resources;
import android.icu.text.DateFormatSymbols;
+import android.icu.text.DecimalFormatSymbols;
import com.android.i18n.timezone.ZoneInfoData;
-import libcore.icu.LocaleData;
-
import java.nio.CharBuffer;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -53,17 +52,17 @@
private static final int DAYSPERNYEAR = 365;
/**
- * The Locale for which the cached LocaleData and formats have been loaded.
+ * The Locale for which the cached symbols and formats have been loaded.
*/
private static Locale sLocale;
private static DateFormatSymbols sDateFormatSymbols;
- private static LocaleData sLocaleData;
+ private static DecimalFormatSymbols sDecimalFormatSymbols;
private static String sTimeOnlyFormat;
private static String sDateOnlyFormat;
private static String sDateTimeFormat;
private final DateFormatSymbols dateFormatSymbols;
- private final LocaleData localeData;
+ private final DecimalFormatSymbols decimalFormatSymbols;
private final String dateTimeFormat;
private final String timeOnlyFormat;
private final String dateOnlyFormat;
@@ -78,7 +77,7 @@
if (sLocale == null || !(locale.equals(sLocale))) {
sLocale = locale;
sDateFormatSymbols = DateFormat.getIcuDateFormatSymbols(locale);
- sLocaleData = LocaleData.get(locale);
+ sDecimalFormatSymbols = DecimalFormatSymbols.getInstance(locale);
Resources r = Resources.getSystem();
sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
@@ -87,10 +86,10 @@
}
this.dateFormatSymbols = sDateFormatSymbols;
+ this.decimalFormatSymbols = sDecimalFormatSymbols;
this.dateTimeFormat = sDateTimeFormat;
this.timeOnlyFormat = sTimeOnlyFormat;
this.dateOnlyFormat = sDateOnlyFormat;
- localeData = sLocaleData;
}
}
@@ -172,12 +171,12 @@
}
private String localizeDigits(String s) {
- if (localeData.zeroDigit == '0') {
+ if (decimalFormatSymbols.getZeroDigit() == '0') {
return s;
}
int length = s.length();
- int offsetToLocalizedDigits = localeData.zeroDigit - '0';
+ int offsetToLocalizedDigits = decimalFormatSymbols.getZeroDigit() - '0';
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
char ch = s.charAt(i);
@@ -220,35 +219,44 @@
char currentChar = formatBuffer.get(formatBuffer.position());
switch (currentChar) {
case 'A':
- modifyAndAppend((wallTime.getWeekDay() < 0
- || wallTime.getWeekDay() >= DAYSPERWEEK)
- ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
+ modifyAndAppend(
+ (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?"
+ : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.WIDE)[wallTime.getWeekDay() + 1],
modifier);
return false;
case 'a':
- modifyAndAppend((wallTime.getWeekDay() < 0
- || wallTime.getWeekDay() >= DAYSPERWEEK)
- ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
+ modifyAndAppend(
+ (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?"
+ : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.ABBREVIATED)[wallTime.getWeekDay() + 1],
modifier);
return false;
case 'B':
if (modifier == '-') {
- modifyAndAppend((wallTime.getMonth() < 0
- || wallTime.getMonth() >= MONSPERYEAR)
- ? "?"
- : localeData.longStandAloneMonthNames[wallTime.getMonth()],
+ modifyAndAppend(
+ (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.STANDALONE,
+ DateFormatSymbols.WIDE)[wallTime.getMonth()],
modifier);
} else {
- modifyAndAppend((wallTime.getMonth() < 0
- || wallTime.getMonth() >= MONSPERYEAR)
- ? "?" : localeData.longMonthNames[wallTime.getMonth()],
+ modifyAndAppend(
+ (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.WIDE)[wallTime.getMonth()],
modifier);
}
return false;
case 'b':
case 'h':
modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
- ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.ABBREVIATED)[wallTime.getMonth()],
modifier);
return false;
case 'C':
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 064bc69..713cfb4 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -311,9 +311,6 @@
}
final int count = focusables.size();
- if (count < 2) {
- return null;
- }
switch (direction) {
case View.FOCUS_FORWARD:
return getNextFocusable(focused, focusables, count);
@@ -376,29 +373,29 @@
}
private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.lastIndexOf(focused);
if (position >= 0 && position + 1 < count) {
return focusables.get(position + 1);
}
}
- return focusables.get(0);
+ if (!focusables.isEmpty()) {
+ return focusables.get(0);
+ }
+ return null;
}
private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.indexOf(focused);
if (position > 0) {
return focusables.get(position - 1);
}
}
- return focusables.get(count - 1);
+ if (!focusables.isEmpty()) {
+ return focusables.get(count - 1);
+ }
+ return null;
}
private static View getNextKeyboardNavigationCluster(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fe6f33d..6552e30 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4032,7 +4032,7 @@
yOffset -= surfaceInsets.top;
// Offset dirty rect for surface insets.
- dirty.offset(surfaceInsets.left, surfaceInsets.right);
+ dirty.offset(surfaceInsets.left, surfaceInsets.top);
}
boolean accessibilityFocusDirty = false;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index fbfeda6..b398cf6 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -727,16 +727,22 @@
mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
if (mSessionId != NO_SESSION) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
final AutofillClient client = getClient();
if (client != null) {
final SyncResultReceiver receiver = new SyncResultReceiver(
SYNC_CALLS_TIMEOUT_MS);
try {
- mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
- mServiceClient.asBinder(), receiver);
- final boolean sessionWasRestored = receiver.getIntResult() == 1;
+ boolean sessionWasRestored = false;
+ if (clientAdded) {
+ mService.restoreSession(mSessionId,
+ client.autofillClientGetActivityToken(),
+ mServiceClient.asBinder(), receiver);
+ sessionWasRestored = receiver.getIntResult() == 1;
+ } else {
+ Log.w(TAG, "No service client for session " + mSessionId);
+ }
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -850,8 +856,8 @@
if (isDisabledByServiceLocked()) {
return false;
}
- ensureServiceClientAddedIfNeededLocked();
- return mEnabled;
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+ return clientAdded ? mEnabled : false;
}
}
@@ -1007,7 +1013,12 @@
AutofillCallback callback = null;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
@@ -1060,9 +1071,10 @@
@GuardedBy("mLock")
void notifyViewExitedLocked(@NonNull View view) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// dont notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
@@ -1178,7 +1190,12 @@
AutofillCallback callback = null;
if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) {
@@ -1241,9 +1258,10 @@
@GuardedBy("mLock")
private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// don't notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
@@ -1905,11 +1923,16 @@
}
}
+ /**
+ * Tries to add AutofillManagerClient to service if it does not been added. Returns {@code true}
+ * if the AutofillManagerClient is added successfully or is already added. Otherwise,
+ * returns {@code false}.
+ */
@GuardedBy("mLock")
- private void ensureServiceClientAddedIfNeededLocked() {
+ private boolean tryAddServiceClientIfNeededLocked() {
final AutofillClient client = getClient();
if (client == null) {
- return;
+ return false;
}
if (mServiceClient == null) {
@@ -1924,7 +1947,10 @@
flags = receiver.getIntResult();
} catch (SyncResultReceiver.TimeoutException e) {
Log.w(TAG, "Failed to initialize autofill: " + e);
- return;
+ // Reset the states initialized above.
+ mService.removeClient(mServiceClient, userId);
+ mServiceClient = null;
+ return false;
}
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
@@ -1949,6 +1975,7 @@
throw e.rethrowFromSystemServer();
}
}
+ return true;
}
@GuardedBy("mLock")
@@ -1962,12 +1989,13 @@
&& view.isLaidOut()
&& view.isVisibleToUser()) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
if (sVerbose) {
- Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
+ Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled + " mServiceClient="
+ + mServiceClient);
}
- if (mEnabled && !isClientDisablingEnterExitEvent()) {
+ if (clientAdded && mEnabled && !isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
final AutofillValue value = view.getAutofillValue();
// Starts new session.
@@ -2692,6 +2720,7 @@
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
pw.print(pfx); pw.print("context: "); pw.println(mContext);
+ pw.print(pfx); pw.print("service client: "); pw.println(mServiceClient);
final AutofillClient client = getClient();
if (client != null) {
pw.print(pfx); pw.print("client: "); pw.print(client);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1c75232..052bca5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1511,7 +1511,7 @@
*
* @param hosts the list of hosts
* @param callback will be called with {@code true} if hosts are successfully added to the
- * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+ * allowlist. It will be called with {@code false} if any hosts are malformed. The callback
* will be run on the UI thread
*/
public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 150fa88..7b6e1a3 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -173,8 +173,9 @@
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
@@ -211,8 +212,9 @@
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 1b899db..33e64f4 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -38,8 +38,6 @@
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Locale;
/**
@@ -264,7 +262,7 @@
mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
DEFAULT_SHOW_WEEK_NUMBER);
mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ Calendar.getInstance().getFirstDayOfWeek());
final String minDate = a.getString(R.styleable.CalendarView_minDate);
if (!CalendarView.parseDate(minDate, mMinDate)) {
CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate);
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 67fef13..7de2bd1 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -33,10 +33,6 @@
import com.android.internal.widget.ViewPager;
import com.android.internal.widget.ViewPager.OnPageChangeListener;
-import libcore.icu.LocaleData;
-
-import java.util.Locale;
-
class DayPickerView extends ViewGroup {
private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material;
private static final int DEFAULT_START_YEAR = 1900;
@@ -86,7 +82,7 @@
attrs, a, defStyleAttr, defStyleRes);
final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ Calendar.getInstance().getFirstDayOfWeek());
final String minDate = a.getString(R.styleable.CalendarView_minDate);
final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 23bbe69..89206fda 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1142,7 +1142,7 @@
bitmapRenderNode.setOutline(outline);
bitmapRenderNode.setClipToOutline(true);
- // Create a dummy draw, which will be replaced later with real drawing.
+ // Create a placeholder draw, which will be replaced later with real drawing.
final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
mContentWidth, mContentHeight);
try {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index baaf2a7..3b482a8 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -34,6 +34,7 @@
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.icu.text.DecimalFormatSymbols;
import android.os.Build;
import android.os.Bundle;
import android.text.InputFilter;
@@ -61,8 +62,6 @@
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -209,7 +208,7 @@
}
private static char getZeroDigit(Locale locale) {
- return LocaleData.get(locale).zeroDigit;
+ return DecimalFormatSymbols.getInstance(locale).getZeroDigit();
}
private java.util.Formatter createFormatter(Locale locale) {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 61c77bc..695a253 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -27,6 +27,7 @@
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.icu.text.DateFormatSymbols;
import android.icu.text.DisplayContext;
import android.icu.text.RelativeDateTimeFormatter;
import android.icu.text.SimpleDateFormat;
@@ -50,8 +51,6 @@
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
-import libcore.icu.LocaleData;
-
import java.text.NumberFormat;
import java.util.Locale;
@@ -194,7 +193,8 @@
private void updateDayOfWeekLabels() {
// Use tiny (e.g. single-character) weekday names from ICU. The indices
// for this list correspond to Calendar days, e.g. SUNDAY is index 1.
- final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames;
+ final String[] tinyWeekdayNames = DateFormatSymbols.getInstance(mLocale)
+ .getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW);
for (int i = 0; i < DAYS_IN_WEEK; i++) {
mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1];
}
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 6432438..95c0e86 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -30,6 +30,7 @@
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.database.ContentObserver;
+import android.icu.text.DateTimePatternGenerator;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
@@ -43,8 +44,6 @@
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Calendar;
import java.util.TimeZone;
@@ -262,14 +261,11 @@
}
private void init() {
- if (mFormat12 == null || mFormat24 == null) {
- LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
- if (mFormat12 == null) {
- mFormat12 = ld.timeFormat_hm;
- }
- if (mFormat24 == null) {
- mFormat24 = ld.timeFormat_Hm;
- }
+ if (mFormat12 == null) {
+ mFormat12 = getBestDateTimePattern("hm");
+ }
+ if (mFormat24 == null) {
+ mFormat24 = getBestDateTimePattern("Hm");
}
createTime(mTimeZone);
@@ -510,13 +506,11 @@
private void chooseFormat() {
final boolean format24Requested = is24HourModeEnabled();
- LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
-
if (format24Requested) {
- mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm);
+ mFormat = abc(mFormat24, mFormat12, getBestDateTimePattern("Hm"));
mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat);
} else {
- mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm);
+ mFormat = abc(mFormat12, mFormat24, getBestDateTimePattern("hm"));
mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat);
}
@@ -529,6 +523,12 @@
}
}
+ private String getBestDateTimePattern(String skeleton) {
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ getContext().getResources().getConfiguration().locale);
+ return dtpg.getBestPattern(skeleton);
+ }
+
/**
* Returns a if not null, else return b if not null, else return c.
*/
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index d238d0e..ea3d2de 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -120,6 +120,13 @@
*/
public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
+ // Flag related to Privacy Indicators
+
+ /**
+ * Whether the Permissions Hub is showing.
+ */
+ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";
+
// Flags related to Assistant
/**
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
index e78e32b..9979e60 100644
--- a/core/java/com/android/internal/listeners/ListenerExecutor.java
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -40,7 +40,7 @@
/**
* Called before this operation is to be run. Some operations may be canceled before they
* are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
- * will only be run if this method was run.
+ * will only be run if this method was run. This callback is invoked on the calling thread.
*/
default void onPreExecute() {}
@@ -49,7 +49,7 @@
* RuntimeException, which will propagate normally. Implementations of
* {@link ListenerExecutor} have the option to override
* {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
- * intercept failures at the class level.
+ * intercept failures at the class level. This callback is invoked on the executor thread.
*/
default void onFailure(Exception e) {
// implementations should handle any exceptions that may be thrown
@@ -59,21 +59,24 @@
/**
* Called after the operation is run. This method will always be called if
* {@link #onPreExecute()} is called. Success implies that the operation was run to
- * completion with no failures.
+ * completion with no failures. This callback may be invoked on the calling thread or
+ * executor thread.
*/
default void onPostExecute(boolean success) {}
/**
* Called after this operation is complete (which does not imply that it was necessarily
* run). Will always be called once per operation, no matter if the operation was run or
- * not. Success implies that the operation was run to completion with no failures.
+ * not. Success implies that the operation was run to completion with no failures. This
+ * callback may be invoked on the calling thread or executor thread.
*/
default void onComplete(boolean success) {}
}
/**
* May be override to handle operation failures at a class level. Will not be invoked in the
- * event of a RuntimeException, which will propagate normally.
+ * event of a RuntimeException, which will propagate normally. This callback is invoked on the
+ * executor thread.
*/
default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
Exception exception) {
@@ -83,7 +86,8 @@
/**
* Executes the given listener operation on the given executor, using the provided listener
* supplier. If the supplier returns a null value, or a value during the operation that does not
- * match the value prior to the operation, then the operation is considered canceled.
+ * match the value prior to the operation, then the operation is considered canceled. If a null
+ * operation is supplied, nothing happens.
*/
default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
@Nullable ListenerOperation<TListener> operation) {
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index a18943c..f83c5bd 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -101,7 +101,7 @@
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
- parent, targetSdkVersion, isNamespaceShared, classLoaderName, null);
+ parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null);
}
@@ -111,18 +111,24 @@
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
classLoaderName, sharedLibraries);
+ String sonameList = "";
+ if (nativeSharedLibraries != null) {
+ sonameList = String.join(":", nativeSharedLibraries);
+ }
+
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
String errorMessage = createClassloaderNamespace(classLoader,
targetSdkVersion,
librarySearchPath,
libraryPermittedPath,
isNamespaceShared,
- dexPath);
+ dexPath,
+ sonameList);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (errorMessage != null) {
@@ -139,5 +145,6 @@
String librarySearchPath,
String libraryPermittedPath,
boolean isNamespaceShared,
- String dexPath);
+ String dexPath,
+ String sonameList);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 0eb3981..c6a1153 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -49,10 +49,12 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -104,11 +106,17 @@
public final String name;
public final String filename;
public final String[] dependencies;
+ public final boolean isNative;
SharedLibraryEntry(String name, String filename, String[] dependencies) {
+ this(name, filename, dependencies, false /* isNative */);
+ }
+
+ SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) {
this.name = name;
this.filename = filename;
this.dependencies = dependencies;
+ this.isNative = isNative;
}
}
@@ -170,12 +178,6 @@
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
- // These are the packages that are whitelisted to be able to run as system user
- final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
-
- // These are the packages that should not run under system user
- final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
-
// These are the components that are enabled by default as VR mode listener services.
final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
@@ -309,14 +311,6 @@
return mLinkedApps;
}
- public ArraySet<String> getSystemUserWhitelistedApps() {
- return mSystemUserWhitelistedApps;
- }
-
- public ArraySet<String> getSystemUserBlacklistedApps() {
- return mSystemUserBlacklistedApps;
- }
-
public ArraySet<String> getHiddenApiWhitelistedApps() {
return mHiddenApiPackageWhitelist;
}
@@ -457,6 +451,7 @@
log.traceBegin("readAllPermissions");
try {
readAllPermissions();
+ readPublicNativeLibrariesList();
} finally {
log.traceEnd();
}
@@ -895,34 +890,6 @@
}
XmlUtils.skipCurrentTag(parser);
} break;
- case "system-user-whitelisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserWhitelistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
- case "system-user-blacklisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserBlacklistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
case "default-enabled-vr-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
@@ -1513,6 +1480,37 @@
}
}
+ private void readPublicNativeLibrariesList() {
+ readPublicLibrariesListFile(new File("/vendor/etc/public.libraries.txt"));
+ String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc"};
+ for (String dir : dirs) {
+ for (File f : (new File(dir)).listFiles()) {
+ String name = f.getName();
+ if (name.startsWith("public.libraries-") && name.endsWith(".txt")) {
+ readPublicLibrariesListFile(f);
+ }
+ }
+ }
+ }
+
+ private void readPublicLibrariesListFile(File listFile) {
+ try (BufferedReader br = new BufferedReader(new FileReader(listFile))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ // Line format is <soname> [abi]. We take the soname part.
+ String soname = line.trim().split(" ")[0];
+ SharedLibraryEntry entry = new SharedLibraryEntry(
+ soname, soname, new String[0], true);
+ mSharedLibraries.put(entry.name, entry);
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read public libraries file " + listFile, e);
+ }
+ }
+
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3da2fa2..d6e8531 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -853,9 +853,8 @@
} cfg_state = CONFIG_UNKNOWN;
if (cfg_state == CONFIG_UNKNOWN) {
- auto runtime_info = vintf::VintfObject::GetInstance()
- ->getRuntimeInfo(false /* skip cache */,
- vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+ auto runtime_info = vintf::VintfObject::GetInstance()->getRuntimeInfo(
+ vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221";
const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs();
std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK");
diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp
index 9379ea6..b0271b9 100644
--- a/core/jni/android_os_VintfRuntimeInfo.cpp
+++ b/core/jni/android_os_VintfRuntimeInfo.cpp
@@ -29,14 +29,12 @@
using vintf::RuntimeInfo;
using vintf::VintfObject;
-#define MAP_STRING_METHOD(javaMethod, cppString, flags) \
- static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) \
- { \
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo( \
- false /* skipCache */, flags); \
- if (info == nullptr) return nullptr; \
- return env->NewStringUTF((cppString).c_str()); \
- } \
+#define MAP_STRING_METHOD(javaMethod, cppString, flags) \
+ static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) { \
+ std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(flags); \
+ if (info == nullptr) return nullptr; \
+ return env->NewStringUTF((cppString).c_str()); \
+ }
MAP_STRING_METHOD(getCpuInfo, info->cpuInfo(), RuntimeInfo::FetchFlag::CPU_INFO);
MAP_STRING_METHOD(getOsName, info->osName(), RuntimeInfo::FetchFlag::CPU_VERSION);
@@ -54,8 +52,8 @@
static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz)
{
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(
- false /* skipCache */, RuntimeInfo::FetchFlag::POLICYVERS);
+ std::shared_ptr<const RuntimeInfo> info =
+ VintfObject::GetRuntimeInfo(RuntimeInfo::FetchFlag::POLICYVERS);
if (info == nullptr) return 0;
return static_cast<jlong>(info->kernelSepolicyVersion());
}
diff --git a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
index f8d41e4..59c413f 100644
--- a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
+++ b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
@@ -28,16 +28,19 @@
jstring librarySearchPath,
jstring libraryPermittedPath,
jboolean isShared,
- jstring dexPath) {
+ jstring dexPath,
+ jstring sonameList) {
return android::CreateClassLoaderNamespace(env, targetSdkVersion,
classLoader, isShared == JNI_TRUE,
dexPath,
- librarySearchPath, libraryPermittedPath);
+ librarySearchPath,
+ libraryPermittedPath,
+ sonameList);
}
static const JNINativeMethod g_methods[] = {
{ "createClassloaderNamespace",
- "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Ljava/lang/String;",
+ "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
reinterpret_cast<void*>(createClassloaderNamespace_native) },
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fe290f3..bae3bf90 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -355,6 +355,7 @@
<protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER" />
<protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER" />
<protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_CLICKED" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3b61c36..03c682f 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk kieslys om oop te sluit of maak noodoproep."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk kieslys om oop te maak."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teken patroon om te ontsluit"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Keer terug na oproep"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Reg!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probeer weer"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9ce214f..f80da89 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ለመክፈት ምናሌ ተጫንወይም የአደጋ ጊዜ ጥሪ አድርግ።"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ለመክፈት ምናሌ ተጫን"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ለመክፈት ስርዓተ ጥለት ሳል"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ድንገተኛ አደጋ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ወደ ጥሪ ተመለስ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ትክክል!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"እንደገና ሞክር"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e80720d..41e3e26 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -844,7 +844,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"اضغط على \"القائمة\" لإلغاء التأمين أو إجراء اتصال بالطوارئ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"رسم نقش لإلغاء التأمين"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"الطوارئ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"العودة إلى الاتصال"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحيح!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"أعد المحاولة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 881a127..c6c8bb5 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক কৰিবলৈ বা জৰুৰীকালীন কল কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক কৰিবলৈ আর্হি আঁকক"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জৰুৰীকালীন"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"কললৈ উভতি যাওক"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"শুদ্ধ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আকৌ চেষ্টা কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6408ef6..f0ff883 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Təcili zəng kilidini açmaq və ya yerləşdirmək üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilidi açmaq üçün model çəkin"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Təcili"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zəngə qayıt"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Düzdür!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bir də cəhd edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2bcf909..8319da7 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string>
@@ -1145,7 +1146,7 @@
<string name="capital_off" msgid="7443704171014626777">"NE"</string>
<string name="checked" msgid="9179896827054513119">"označeno je"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Dovršavanje radnje pomoću"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Otvorite pomoću"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index ed781fb..3540516 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Націсніце \"Меню\", каб разблакаваць, або зрабіце экстраны выклік."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Націсніце \"Меню\", каб разблакаваць."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намалюйце камбінацыю разблакоўкі, каб разблакаваць"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстранны выклік"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вярнуцца да выкліку"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правільна!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Паспрабуйце яшчэ раз"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index f089d62..6643bcf 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натиснете „Меню“, за да отключите или да извършите спешно обаждане."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натиснете „Меню“, за да отключите."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Нарисувайте фигура, за да отключите"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Спешни случаи"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад към обаждането"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правилно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Опитайте отново"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 1ac213f0..cbec4ac 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক করতে বা জরুরি কল করতে মেনু টিপুন৷"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক করতে মেনু টিপুন৷"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক করতে প্যাটার্ন আঁকুন"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জরুরী"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"কলে ফিরুন"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"সঠিক!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আবার চেষ্টা করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f27441a..87b02f5 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite dugme Meni kako biste otključali uređaj ili obavili hitni poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite dugme Meni za otključavanje uređaja."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nacrtajte uzorak za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitno"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Povratak na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
@@ -1145,8 +1146,8 @@
<string name="capital_off" msgid="7443704171014626777">"Isključeno"</string>
<string name="checked" msgid="9179896827054513119">"označeno"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Izvrši akciju koristeći"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršite akciju koristeći %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Otvori koristeći"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"Otvori koristeći %1$s"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8561764..c69ca37d 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chcete-li odemknout telefon nebo provést tísňové volání, stiskněte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefon odemknete stisknutím tlačítka Menu."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odblokujte pomocí gesta"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav nouze"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolat zpět"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správně!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zkusit znovu"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 1b3dab2..fb10077 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryk på Menu for at låse op eller foretage et nødopkald."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryk på Menu for at låse op."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn oplåsningsmønster"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødsituation"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbage til opkald"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rigtigt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv igen"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 296200f..4adcf0a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Drücke die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Zum Entsperren die Menütaste drücken"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Muster zum Entsperren zeichnen"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Notfall"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zurück zum Anruf"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Erneut versuchen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e790c9a..23895f4 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Πατήστε \"Menu\" για ξεκλείδωμα ή για κλήση έκτακτης ανάγκης."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Σχεδιασμός μοτίβου για ξεκλείδωμα"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Κλήση έκτακτης ανάγκης"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Επιστροφή στην κλήση"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Σωστό!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Προσπαθήστε ξανά"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0f1566b..0484ccd 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 84b3ce1..9981c12 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index d9a2bdb..02cdf69a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1209f2b..3b18aca 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7f29fd4..6503f7c 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -832,7 +832,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Emergency call"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f8ead279..7e8a89a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Presionar Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar el patrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regresar a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 741c89f..02aafa7 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar patrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index d724046..8136294 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Vajutage avamiseks või hädaabikõne tegemiseks menüünuppu"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Vajutage avamiseks menüüklahvi."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Avamiseks joonistage muster"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hädaabi"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kõne juurde tagasi"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Õige."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Proovige uuesti"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 24d3eef..1b88f27 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Desblokeatzeko edo larrialdi-deia egiteko, sakatu Menua."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Desblokeatzeko, sakatu Menua."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desblokeatzeko, marraztu eredua"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Larrialdi-deiak"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Itzuli deira"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Eredua zuzena da!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Saiatu berriro"</string>
@@ -846,7 +847,7 @@
<string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"Sartu SIM txartela."</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"SIM txartela falta da edo ezin da irakurri. Sartu SIM txartel bat."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="3812893366715730539">"SIM txartela hondatuta dago."</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela behin betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Aurreko pista"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Hurrengo pista"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b0b5338..506a7ee 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"برای بازگشایی قفل یا انجام تماس اضطراری روی منو فشار دهید."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"برای بازگشایی قفل روی منو فشار دهید."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"الگو را بکشید تا قفل آن باز شود"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"اضطراری"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"بازگشت به تماس"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح است!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوباره امتحان کنید"</string>
@@ -1125,8 +1126,8 @@
<string name="capital_off" msgid="7443704171014626777">"خاموش"</string>
<string name="checked" msgid="9179896827054513119">"علامتزدهشده"</string>
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
- <string name="whichApplication" msgid="5432266899591255759">"تکمیل عملکرد با استفاده از"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"تکمیل عملکرد با استفاده از %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"تکمیل کنش بااستفاده از %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"باز کردن با"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"باز کردن با %1$s"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 815daaa..01db781 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Poista lukitus tai soita hätäpuhelu painamalla Valikko-painiketta."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Poista lukitus painamalla Valikko-painiketta."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Poista lukitus piirtämällä kuvio"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hätäpuhelu"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Palaa puheluun"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Oikein!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Yritä uudelleen"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index d3323d7..f5adc77 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour débloquer le téléphone ou appeler un numéro d\'urgence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgence"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"C\'est exact!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Réessayer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 4a1646f..8880dc6 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour déverrouiller le téléphone ou appeler un numéro d\'urgence"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgences"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Combinaison correcte !"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Veuillez réessayer."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 54e17fa..dffca1c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Preme Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Crea o padrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emerxencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver á chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Téntao de novo"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index be17cfd..784e85f 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"અનલૉક કરવા માટે અથવા કટોકટીનો કૉલ કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"અનલૉક કરવા માટે પૅટર્ન દોરો."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ઇમર્જન્સી"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"કૉલ પર પાછા ફરો"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"સાચું!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ફરી પ્રયાસ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 97118f8..82a1062 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"लॉक खोलने के लिए मेन्यू दबाएं या आपातलकालीन कॉल करें."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"लॉक खोलने के लिए मेन्यू दबाएं."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करने के लिए आकार आरेखित करें"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपातकाल"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉल पर वापस लौटें"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फिर से कोशिश करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index a1b303e..f1bcd39 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite Izbornik za otključavanje ili pozivanje hitnih službi."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite Izbornik za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iscrtajte uzorak za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Uzvrati poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ff0d470..3106371 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"A feloldáshoz vagy segélyhívás kezdeményezéséhez nyomja meg a Menü gombot."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"A feloldáshoz nyomja meg a Menü gombot."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rajzolja le a mintát a feloldáshoz"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Segélyhívás"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Hívás folytatása"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Helyes!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Próbálja újra"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 05c447d..85afc27 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ապակողպելու կամ շտապ կանչ անելու համար սեղմեք «Ընտրացանկ»"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Հավաքեք սխեման` ապակողպելու համար"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Շտապ կանչ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Վերադառնալ զանգին"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ճիշտ է:"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Կրկին փորձեք"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index a6f68d1..8ec1f50 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk membuka atau melakukan panggilan darurat."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Buat pola untuk membuka"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Darurat"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Perbaiki!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Coba lagi"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"MATI"</string>
<string name="checked" msgid="9179896827054513119">"dicentang"</string>
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Tindakan lengkap menggunakan"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Buka dengan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 92f6f24..1b0c2fe 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ýttu á valmyndartakkann til að taka úr lás eða hringja neyðarsímtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ýttu á valmyndartakkann til að taka úr lás."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teiknaðu mynstur til að taka úr lás"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Neyðarsímtal"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Aftur í símtal"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rétt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Reyndu aftur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 8f857fa..d2bf62d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Premi Menu per sbloccare."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Traccia la sequenza di sblocco"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergenza"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Torna a chiamata"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corretta."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Riprova"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 27889fb..f9674bd 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"MENUキーでロック解除(または緊急通報)"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"MENUキーでロック解除"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"パターンを入力"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急通報"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"通話に戻る"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"一致しました"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"もう一度お試しください"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6547185..3d1e9c7 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"განბლოკვისთვის ან გადაუდებელი ზარისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"განსაბლოკად დახატეთ ნიმუში"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"საგანგებო სამსახურები"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ზარზე დაბრუნება"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"სწორია!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"კიდევ სცადეთ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 3d53912..38a50ab 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Бекітпесін ашу үшін немесе төтенше қоңырауды табу үшін Мәзір тармағын басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ашу үшін Мәзір пернесін басыңыз."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Бекітпесін ашу үшін кескінді сызыңыз"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Төтенше жағдай"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Қоңырауға оралу"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Дұрыс!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Қайталап көріңіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 760d256..4e646c5 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ចុចម៉ឺនុយ ដើម្បីដោះសោ ឬហៅពេលអាសន្ន។"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ចុចម៉ឺនុយ ដើម្បីដោះសោ។"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"គូរលំនាំ ដើម្បីដោះសោ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"បន្ទាន់"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ត្រឡប់ទៅការហៅ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ត្រឹមត្រូវ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ព្យាយាមម្ដងទៀត"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 6513481..8d74215 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ ಇಲ್ಲವೇ ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಪ್ಯಾಟರ್ನ್ ಚಿತ್ರಿಸಿ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ತುರ್ತು"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ಕರೆಗೆ ಹಿಂತಿರುಗು"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ಸರಿಯಾಗಿದೆ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index db8d379..5d960dc 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"잠금해제를 위해 패턴 그리기"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"긴급 전화"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"통화로 돌아가기"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"맞습니다."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"다시 시도"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index c208396..c97324f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Кулпусун ачып же Шашылыш чалуу аткаруу үчүн менюну басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Бөгөттөн чыгаруу үчүн Менюну басыңыз."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Кулпуну ачуу үчүн, үлгүнү тартыңыз"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Тез жардам"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Чалууга кайтуу"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Туура!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дагы аракет кылыңыз"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ӨЧҮК"</string>
<string name="checked" msgid="9179896827054513119">"белгиленген"</string>
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Аракет колдонууну бүтүрүү"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Төмөнкү менен ачуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index f388cfa..c1b3fe4 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ກົດ ເມນູ ເພື່ອປົດລັອກ ຫຼື ໂທອອກຫາເບີສຸກເສີນ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ແຕ້ມຮູບແບບເພື່ອປົດລັອກ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ສຸກເສີນ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ກັບໄປຫາການໂທ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ຖືກຕ້ອງ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ລອງໃໝ່ອີກຄັ້ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index d68cd8d..fc5709c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Paspauskite „Meniu“, kad atrakintumėte ar skambintumėte pagalbos numeriu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Paspauskite „Meniu“, jei norite atrakinti."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nustatyti modelį, kad atrakintų"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Skambutis pagalbos numeriu"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"grįžti prie skambučio"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Teisingai!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bandykite dar kartą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 9462c22..d780b14 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Zīmējiet kombināciju, lai atbloķētu."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Ārkārtas situācija"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Atpakaļ pie zvana"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pareizi!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Mēģināt vēlreiz"</string>
diff --git a/core/res/res/values-mcc260/config.xml b/core/res/res/values-mcc260/config.xml
new file mode 100644
index 0000000..79eefb7
--- /dev/null
+++ b/core/res/res/values-mcc260/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <!-- Set to false to disable emergency alert. -->
+ <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-mcc262/config.xml b/core/res/res/values-mcc262/config.xml
new file mode 100644
index 0000000..79eefb7
--- /dev/null
+++ b/core/res/res/values-mcc262/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <!-- Set to false to disable emergency alert. -->
+ <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index aee1fca..b50c608 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисни „Мени“ да се отклучи или да направи итен повик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притиснете „Мени“ за да се отклучи."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Употребете ја шемата за да се отклучи"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Итен случај"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Врати се на повик"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Точно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Обидете се повторно"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ИСКЛУЧЕНО"</string>
<string name="checked" msgid="9179896827054513119">"штиклирано"</string>
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Заврши дејство со"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Отвори со"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 375cb98..b769fd2 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"അൺലോക്ക് ചെയ്യുന്നതിനായി മെനു അമർത്തുക അല്ലെങ്കിൽ അടിയന്തര കോൾ വിളിക്കുക."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"അൺലോക്ക് ചെയ്യാൻ പാറ്റേൺ വരയ്ക്കുക"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"എമർജൻസി"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"കോളിലേക്ക് മടങ്ങുക"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ശരി!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"വീണ്ടും ശ്രമിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 1472c4e..7b41501 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Тайлах хээгээ зурна уу"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Яаралтай тусламж"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Дуудлагаруу буцах"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Зөв!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дахин оролдох"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 6695de6..5778033 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलॉक करण्यासाठी मेनू दाबा किंवा आणीबाणीचा कॉल करा."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलॉक करण्यासाठी मेनू दाबा."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करण्यासाठी पॅटर्न काढा"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आणीबाणी"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉलवर परत या"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"अचूक!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"पुन्हा प्रयत्न करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c8f37c3..c30bcfc 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk menyahsekat atau membuat panggilan kecemasan."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka kunci."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Lukiskan corak untuk membuka kunci"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Kecemasan"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Betul!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Cuba lagi"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index dcb4e3c..243357e 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ဖွင့်ရန်သို့မဟုတ်အရေးပေါ်ခေါ်ဆိုခြင်းပြုလုပ်ရန် မီနူးကိုနှိပ်ပါ"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"မီးနူးကို နှိပ်ခြင်းဖြင့် သော့ဖွင့်ပါ"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ဖွင့်ရန်ပုံစံဆွဲပါ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"အရေးပေါ်"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ခေါ်ဆိုမှုထံပြန်သွားရန်"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"မှန်ပါသည်"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ထပ် စမ်းပါ"</string>
@@ -1102,7 +1103,7 @@
<string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
<string name="copyUrl" msgid="6229645005987260230">"URLအား ကူးခြင်း"</string>
<string name="selectTextMode" msgid="3225108910999318778">"စာသား ရွေးရန်"</string>
- <string name="undo" msgid="3175318090002654673">"ပြန်ဖျက်ရန်"</string>
+ <string name="undo" msgid="3175318090002654673">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
<string name="redo" msgid="7231448494008532233">"ထပ်လုပ်ပါ"</string>
<string name="autofill" msgid="511224882647795296">"အော်တိုဖြည့်"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"စာတိုရွေးချယ်မှု"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ပိတ်"</string>
<string name="checked" msgid="9179896827054513119">"အမှန်ခြစ်ပြီး"</string>
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
- <string name="whichApplication" msgid="5432266899591255759">"အသုံးပြု၍ ဆောင်ရွက်မှုအားပြီးဆုံးစေခြင်း"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"...ဖြင့် ဖွင့်မည်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 6926d65..0a31b49 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Trykk på menyknappen for å låse opp."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn mønster for å låse opp"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødssituasjon"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbake til samtale"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Riktig!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv på nytt"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index c9ac6ec..9712b48 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपतकालीन"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 904c906..d1a342c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk op \'Menu\' om te ontgrendelen of noodoproep te plaatsen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk op \'Menu\' om te ontgrendelen."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Patroon tekenen om te ontgrendelen"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Terug naar gesprek"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Juist!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Opnieuw proberen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 62505a3..84815f7 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ଅନଲକ୍ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ କିମ୍ବା ଜରୁରୀକାଳୀନ କଲ୍ କରନ୍ତୁ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ଅନଲକ୍ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ଅନଲକ୍ କରିବା ପାଇଁ ପାଟର୍ନ ଆଙ୍କନ୍ତୁ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ଜରୁରୀକାଳୀନ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"କଲ୍କୁ ଫେରନ୍ତୁ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ଠିକ୍!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 346ee9a..c29308f 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਸੰਕਟਕਾਲੀਨ ਕਾਲ ਕਰੋ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪੈਟਰਨ ਡ੍ਰਾ ਕਰੋ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ਸੰਕਟਕਾਲ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ਕਾਲ ਤੇ ਵਾਪਸ ਜਾਓ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ਸਹੀ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7d2795e..df56457 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Naciśnij Menu, aby odblokować."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Narysuj wzór, aby odblokować"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Alarmowe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Powrót do połączenia"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Poprawnie!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Spróbuj ponownie."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 4fec5b3..3ecf28d 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5f6fb5c..dcc8e17 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -286,7 +286,7 @@
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"Demonstração para retalho"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"Ligação USB"</string>
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicação em execução"</string>
- <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicações que estão a consumir bateria"</string>
+ <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão a consumir bateria"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a consumir bateria."</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicações estão a consumir bateria."</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Prima Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhar padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regressar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tentar novamente"</string>
@@ -1153,7 +1154,7 @@
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagem"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Utilizar por predefinição para esta ação."</string>
<string name="use_a_different_app" msgid="4987790276170972776">"Utilizar outra app"</string>
- <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema > Aplicações > Transferidas."</string>
+ <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema > Apps > Transferidas."</string>
<string name="chooseActivity" msgid="8563390197659779956">"Escolha uma ação"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"Escolher uma app para o dispositivo USB"</string>
<string name="noApplications" msgid="1186909265235544019">"Nenhuma app pode efetuar esta ação."</string>
@@ -1181,7 +1182,7 @@
<string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> foi originalmente iniciado."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Escala"</string>
<string name="screen_compat_mode_show" msgid="5080361367584709857">"Mostrar sempre"</string>
- <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema > Aplicações > Transferidas."</string>
+ <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema > Apps > Transferidas."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> não suporta a definição de Tamanho do ecrã atual e pode ter um comportamento inesperado."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Mostrar sempre"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> foi concebida para uma versão incompatível do SO Android e pode ter um comportamento inesperado. Pode estar disponível uma versão atualizada da app."</string>
@@ -1274,7 +1275,7 @@
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Enviar"</string>
<string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Cancelar"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Memorizar a minha escolha"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições > Aplicações"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições > Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permitir Sempre"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nunca Permitir"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"Cartão SIM removido"</string>
@@ -1284,8 +1285,8 @@
<string name="sim_added_message" msgid="6602906609509958680">"Reinicie o aparelho para aceder à rede de telemóvel."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Reiniciar"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar o serviço móvel"</string>
- <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Transfira a app do operador para ativar o seu novo SIM."</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Transfira a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
+ <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Descarregue a app do operador para ativar o seu novo SIM."</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descarregue a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Transferir app"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo SIM inserido"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
@@ -2035,7 +2036,7 @@
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta app não recebeu autorização de gravação, mas pode capturar áudio através deste dispositivo USB."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Página inicial"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Anterior"</string>
- <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplicações recentes"</string>
+ <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Apps recentes"</string>
<string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Notificações"</string>
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Definições rápidas"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Caixa de diálogo de energia"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4fec5b3..3ecf28d 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index a0e3598..6493668 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apăsați Meniu pentru deblocare."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenați modelul pentru a debloca"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgență"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Reveniți la apel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corect!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încercați din nou"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1a65847..07abf6d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Для разблокировки нажмите \"Меню\"."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Введите графический ключ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстренный вызов"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вернуться к вызову"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторите попытку"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index db759f6..d494a36 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"අගුළු හැරීමට මෙනුව ඔබන්න හෝ හදිසි ඇමතුම ලබාගන්න."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"අගුළු හැරීමට මෙනු ඔබන්න."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"අගුළු ඇරීමට රටාව අඳින්න"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"හදිසි"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ඇමතුම වෙත නැවත යන්න"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"නිවැරදියි!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"නැවත උත්සාහ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 5f80364..52c5301 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefón odomknete stlačením tlačidla Menu."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odomknite nakreslením vzoru"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav tiesne"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolať späť"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správne!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Skúsiť znova"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4e37059..a43c900 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Če želite odkleniti napravo ali opraviti klic v sili, pritisnite meni."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Če želite odkleniti, pritisnite meni."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Če želite odkleniti, narišite vzorec"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Klic v sili"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazaj na klic"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pravilno."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Poskusi znova"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 30e75558..625962f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Shtyp \"Meny\" për të shkyçur ose për të kryer telefonatë urgjence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Shtyp \"Meny\" për të shkyçur."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vizato modelin për ta shkyçur"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgjenca"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kthehu te telefonata"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Saktë!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Provo sërish"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6890b80..6101dac 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Хитне службе"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string>
@@ -1145,7 +1146,7 @@
<string name="capital_off" msgid="7443704171014626777">"НЕ"</string>
<string name="checked" msgid="9179896827054513119">"означено је"</string>
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Довршавање радње помоћу"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Отворите помоћу"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 87b0872..e30bb14 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryck på Menu för att låsa upp eller ringa nödsamtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryck på Menu för att låsa upp."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rita grafiskt lösenord för att låsa upp"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nödsamtal"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tillbaka till samtal"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Försök igen"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fbf4cca..4e4c0f6 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Bonyeza Menyu ili kufungua."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Chora ruwaza ili kufungua"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Dharura"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Rudi kwa kupiga simu"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Sahihi!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Jaribu tena"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 731a796..a617248 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"தடைநீக்க மெனுவை அழுத்தவும் அல்லது அவசர அழைப்பை மேற்கொள்ளவும்."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"திறக்க, மெனுவை அழுத்தவும்."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"திறக்க வடிவத்தை வரையவும்"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"அவசர அழைப்பு"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"அழைப்பிற்குத் திரும்பு"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"சரி!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"மீண்டும் முயற்சிக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a2ec787..794fec8 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"అన్లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"అన్లాక్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"అన్లాక్ చేయడానికి నమూనాను గీయండి"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"అత్యవసరం"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"కాల్కు తిరిగి వెళ్లు"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"సరైనది!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"మళ్లీ ప్రయత్నించండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 6773014..3deb9b2 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"กด เมนู เพื่อปลดล็อกหรือโทรฉุกเฉิน"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"กด เมนู เพื่อปลดล็อก"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"วาดรูปแบบเพื่อปลดล็อก"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"เหตุฉุกเฉิน"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"กลับสู่การโทร"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ถูกต้อง!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ลองอีกครั้ง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index da3e437..ccda1c4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pindutin ang Menu upang i-unlock o magsagawa ng pang-emergency na tawag."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pindutin ang Menu upang i-unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iguhit ang pattern upang i-unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Bumalik sa tawag"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tama!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Subukang muli"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bf17f6c..85c6986 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Kilidi açmak veya acil çağrı yapmak için Menü\'ye basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmak için Menü\'ye basın."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilit açmak için deseni çizin"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Acil durum çağrısı"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Çağrıya dön"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Doğru!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tekrar deneyin"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 9464efa..8ae20a5 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натис. меню, щоб розбл. чи зробити авар. виклик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натисн. меню, щоб розбл."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намал. ключ, щоб розбл."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Екстрений виклик"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Поверн. до дзвін."</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторіть спробу"</string>
@@ -1165,7 +1166,7 @@
<string name="capital_off" msgid="7443704171014626777">"ВИМК"</string>
<string name="checked" msgid="9179896827054513119">"вибрано"</string>
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Завершити дію за доп."</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Відкрити за допомогою"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 15a1fe7..94c6cd4 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"غیر مقفل کرنے کیلئے مینو دبائیں یا ہنگامی کال کریں۔"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"غیر مقفل کرنے کیلئے پیٹرن کو ڈرا کریں"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ہنگامی"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"کال پر واپس جائیں"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوبارہ کوشش کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 76d2c60..74676df 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Qulfdan chiqarish yoki favqulodda qo‘ng‘iroqni amalga oshirish uchun \"Menyu\"ni bosing."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Qulfni ochish uchun grafik kalitni chizing"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Favqulodda chaqiruv"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Qo‘ng‘iroqni qaytarish"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"To‘g‘ri!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Qaytadan urining"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"belgilandi"</string>
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Ilovani tanlang"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Ochish…"</string>
@@ -1548,7 +1549,7 @@
<string name="launchBrowserDefault" msgid="6328349989932924119">"Brauzer ishga tushirilsinmi?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"Har doim"</string>
- <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat hozir"</string>
+ <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat shu safar"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"“%1$s” ishchi profilni qo‘llab-quvvatlamaydi"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Planshet"</string>
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9f44841..48f3858 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nhấn vào Menu để mở khóa hoặc thực hiện cuộc gọi khẩn cấp."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Nhấn vào Menu để mở khóa."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vẽ hình để mở khóa"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Khẩn cấp"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Quay lại cuộc gọi"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Chính xác!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Thử lại"</string>
@@ -1125,8 +1126,8 @@
<string name="capital_off" msgid="7443704171014626777">"TẮT"</string>
<string name="checked" msgid="9179896827054513119">"đã chọn"</string>
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất tác vụ đang sử dụng"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất tác vụ bằng %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Mở bằng"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"Mở bằng %1$s"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 32ac0cf..bf381ec 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按 Menu 解锁或进行紧急呼救。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按 MENU 解锁。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"绘制解锁图案"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"紧急呼救"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通话"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正确!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"重试"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 82b079f..a3b20d1 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按選單鍵解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按選單鍵解鎖。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖形以解除鎖定螢幕"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急電話"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 02c0200..f6543c9 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按下 [Menu] 解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按下 Menu 鍵解鎖。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖案"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急撥號"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 7fe333d..85f54e9 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chofoza Menyu ukuvula noma ukwenza ikholi ephuthumayo."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Chofoza Menyu ukuvula."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dweba iphathini ukuvula"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Isimo esiphuthumayo"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Buyela ekholini"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Lungile!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zama futhi"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb30c9b..ac08d96 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2173,6 +2173,29 @@
<attr name="required" />
</declare-styleable>
+ <!-- The <code>uses-native-library</code> specifies a native shared library that this
+ package requires to be linked against. Specifying this flag tells the
+ system to make the native library to be available to your app.
+
+ <p>On devices running R or lower, this is ignored and the app has access to all
+ the public native shared libraries that are exported from the platform. This is
+ also ignored if the app is targeting R or lower.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesNativeLibrary" parent="AndroidManifestApplication">
+ <!-- Required name of the library you use. -->
+ <attr name="name" />
+ <!-- Specify whether this native library is required for the application.
+ The default is true, meaning the application requires the
+ library, and does not want to be installed on devices that
+ don't support it. If you set this to false, then this will
+ allow the application to be installed even if the library
+ doesn't exist, and you will need to check for its presence
+ dynamically at runtime. -->
+ <attr name="required" />
+ </declare-styleable>
+
<!-- The <code>uses-static-library</code> specifies a shared <strong>static</strong>
library that this package requires to be statically linked against. Specifying
this tag tells the system to include this library's code in your class loader.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 28a164f..bc4c099 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2286,7 +2286,7 @@
</integer-array>
<!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
- <bool name="config_cellBroadcastAppLinks">false</bool>
+ <bool name="config_cellBroadcastAppLinks">true</bool>
<!-- The default value if the SyncStorageEngine should sync automatically or not -->
<bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
index c6d6d1f..b50792c 100644
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ b/core/tests/coretests/src/android/graphics/PathTest.java
@@ -28,7 +28,9 @@
final Path.FillType defaultFillType = path.getFillType();
final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
- assertFalse(fillType.equals(defaultFillType)); // Sanity check for the test itself.
+
+ // This test is only meaningful if it changes from the default.
+ assertFalse(fillType.equals(defaultFillType));
path.setFillType(fillType);
path.reset();
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index a5a2221..ada8b00 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -39,6 +39,7 @@
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.OBSERVE_NETWORK_POLICY"/>
+ <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.READ_DREAM_STATE"/>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index e1f6b2a..dd8f40d 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -263,10 +263,4 @@
be able to connect to the internet when such a proxy is in use, since
all outgoing connections originate from this app. -->
<allow-in-power-save-except-idle package="com.android.proxyhandler" />
-
- <!-- These are the packages that are white-listed to be able to run as system user -->
- <system-user-whitelisted-app package="com.android.settings" />
-
- <!-- These are the packages that shouldn't run as system user -->
- <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
</permissions>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index d92dbb2..4cdfbb8 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -412,6 +412,7 @@
key 583 ASSIST
# Keys defined by HID usages
+key usage 0x0c0067 WINDOW
key usage 0x0c006F BRIGHTNESS_UP
key usage 0x0c0070 BRIGHTNESS_DOWN
diff --git a/data/keyboards/Vendor_2e95_Product_7725.kl b/data/keyboards/Vendor_2e95_Product_7725.kl
new file mode 100644
index 0000000..7672e22
--- /dev/null
+++ b/data/keyboards/Vendor_2e95_Product_7725.kl
@@ -0,0 +1,64 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Scuf Vantage Controller
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# L2 Trigger axis
+axis 0x03 LTRIGGER
+# R2 Trigger axis
+axis 0x04 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index d8d9641..43373ff 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -409,10 +409,10 @@
mNativeRegion = ni;
}
- /* add dummy parameter so constructor can be called from jni without
+ /* Add an unused parameter so constructor can be called from jni without
triggering 'not cloneable' exception */
@UnsupportedAppUsage
- private Region(long ni, int dummy) {
+ private Region(long ni, int unused) {
this(ni);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index cb64796..338ece5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -30,6 +30,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -60,20 +61,20 @@
private static final int DIRECTION_HIDE = 2;
private static final int FLOATING_IME_BOTTOM_INSET = -80;
- protected final SystemWindows mSystemWindows;
+ protected final IWindowManager mWmService;
protected final Handler mHandler;
- final TransactionPool mTransactionPool;
+ private final TransactionPool mTransactionPool;
+ private final DisplayController mDisplayController;
+ private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+ private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
- final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
-
- final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
-
- public DisplayImeController(SystemWindows syswin, DisplayController displayController,
- Handler handler, TransactionPool transactionPool) {
- mHandler = handler;
- mSystemWindows = syswin;
+ public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ Handler mainHandler, TransactionPool transactionPool) {
+ mHandler = mainHandler;
+ mWmService = wmService;
mTransactionPool = transactionPool;
- displayController.addDisplayWindowListener(this);
+ mDisplayController = displayController;
+ mDisplayController.addDisplayWindowListener(this);
}
@Override
@@ -81,9 +82,9 @@
// Add's a system-ui window-manager specifically for ime. This type is special because
// WM will defer IME inset handling to it in multi-window scenarious.
PerDisplay pd = new PerDisplay(displayId,
- mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
+ mDisplayController.getDisplayLayout(displayId).rotation());
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
@@ -96,7 +97,7 @@
if (pd == null) {
return;
}
- if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
+ if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
pd.startAnimation(true, false /* forceRestart */);
}
@@ -105,7 +106,7 @@
@Override
public void onDisplayRemoved(int displayId) {
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -263,7 +264,7 @@
private void setVisibleDirectly(boolean visible) {
mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
try {
- mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
}
}
@@ -282,7 +283,7 @@
// an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
// frame height so any reported frame that is <= nav-bar frame height is assumed to
// be floating.
- return frame.height() <= mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+ return frame.height() <= mDisplayController.getDisplayLayout(mDisplayId)
.navBarFrameHeight();
}
@@ -297,8 +298,7 @@
// This is a "floating" or "expanded" IME, so to get animations, just
// pretend the ime has some size just below the screen.
mImeFrame.set(newFrame);
- final int floatingInset = (int) (
- mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+ final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId)
.density() * FLOATING_IME_BOTTOM_INSET);
mImeFrame.bottom -= floatingInset;
} else if (newFrame.height() != 0) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 34018e8..8abe9ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -60,11 +60,10 @@
private static final String TAG = "SystemWindows";
private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
- final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
- public Context mContext;
- public IWindowManager mWmService;
- IWindowSession mSession;
- DisplayController mDisplayController;
+ private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
+ private final DisplayController mDisplayController;
+ private final IWindowManager mWmService;
+ private IWindowSession mSession;
private final DisplayController.OnDisplaysChangedListener mDisplayListener =
new DisplayController.OnDisplaysChangedListener() {
@@ -84,9 +83,7 @@
public void onDisplayRemoved(int displayId) { }
};
- public SystemWindows(Context context, DisplayController displayController,
- IWindowManager wmService) {
- mContext = context;
+ public SystemWindows(DisplayController displayController, IWindowManager wmService) {
mWmService = wmService;
mDisplayController = displayController;
mDisplayController.addDisplayWindowListener(mDisplayListener);
@@ -210,8 +207,8 @@
}
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot =
- new SurfaceControlViewHost(mContext, display, wwm,
- true /* useSfChoreographer */);
+ new SurfaceControlViewHost(
+ view.getContext(), display, wwm, true /* useSfChoreographer */);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
@@ -313,7 +310,7 @@
}
}
- class ContainerWindow extends IWindow.Stub {
+ static class ContainerWindow extends IWindow.Stub {
ContainerWindow() {}
@Override
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index d25fc4b..b2c39c9 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -139,6 +139,9 @@
(*mGlobalData)->reportJank();
}
+ if (mSwapDeadline < 0) {
+ mSwapDeadline = frame[FrameInfoIndex::IntendedVsync] + mFrameInterval;
+ }
bool isTripleBuffered = (mSwapDeadline - frame[FrameInfoIndex::IntendedVsync]) > (mFrameInterval * 0.1);
mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 4460266..b3fbbfe 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -75,7 +75,7 @@
std::array<int64_t, NUM_BUCKETS> mThresholds;
int64_t mFrameInterval;
- nsecs_t mSwapDeadline;
+ nsecs_t mSwapDeadline = -1;
// The amount of time we will erase from the total duration to account
// for SF vsync offsets with HWC2 blocking dequeueBuffers.
// (Vsync + mDequeueBlockTolerance) is the point at which we expect
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index c0a2443..1a89cfd 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -209,11 +209,8 @@
void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
mInfo = validateAlpha(newInfo);
- // Dirty hack is dirty
- // TODO: Figure something out here, Skia's current design makes this
- // really hard to work with. Skia really, really wants immutable objects,
- // but with the nested-ref-count hackery going on that's just not
- // feasible without going insane trying to figure it out
+ // TODO: Skia intends for SkPixelRef to be immutable, but this method
+ // modifies it. Find another way to support reusing the same pixel memory.
this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 496885c..997339e 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -151,6 +151,16 @@
return status;
}
+ /**
+ * Builds an empty GpsStatus. Should only be used for legacy reasons.
+ *
+ * @hide
+ */
+ @NonNull
+ static GpsStatus createEmpty() {
+ return new GpsStatus();
+ }
+
private GpsStatus() {
}
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 6e7f6a5..29b483a 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -24,6 +24,6 @@
*/
oneway interface ILocationListener
{
- void onLocationChanged(in Location location, in IRemoteCallback onCompleteCallback);
+ void onLocationChanged(in Location location, in @nullable IRemoteCallback onCompleteCallback);
void onProviderEnabledChanged(String provider, boolean enabled);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index bb8f81d..0e7eaa2 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -113,7 +113,7 @@
List<LocationRequest> getTestProviderCurrentRequests(String provider);
LocationTime getGnssTimeMillis();
- boolean sendExtraCommand(String provider, String command, inout Bundle extras);
+ void sendExtraCommand(String provider, String command, inout Bundle extras);
// used by gts tests to verify whitelists
String[] getBackgroundThrottlingWhitelist();
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 8df0834..2738ff4f 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -36,7 +36,9 @@
public interface LocationListener {
/**
- * Called when the location has changed.
+ * Called when the location has changed. A wakelock is held on behalf on the listener for some
+ * brief amount of time as this callback executes. If this callback performs long running
+ * operations, it is the client's responsibility to obtain their own wakelock.
*
* @param location the updated location
*/
@@ -52,18 +54,17 @@
default void onStatusChanged(String provider, int status, Bundle extras) {}
/**
- * Called when the provider is enabled by the user.
+ * Called when a provider this listener is registered with becomes enabled.
*
- * @param provider the name of the location provider that has become enabled
+ * @param provider the name of the location provider
*/
default void onProviderEnabled(@NonNull String provider) {}
/**
- * Called when the provider is disabled by the user. If requestLocationUpdates
- * is called on an already disabled provider, this method is called
- * immediately.
+ * Called when the provider this listener is registered with becomes disabled. If a provider is
+ * disabled when this listener is registered, this callback will be invoked immediately.
*
- * @param provider the name of the location provider that has become disabled
+ * @param provider the name of the location provider
*/
default void onProviderDisabled(@NonNull String provider) {}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c0b8e1b..1803027 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -303,7 +303,6 @@
public static final String METADATA_SETTINGS_FOOTER_STRING =
"com.android.settings.location.FOOTER_STRING";
-
private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
@GuardedBy("sLocationListeners")
@@ -311,7 +310,9 @@
sLocationListeners = new WeakHashMap<>();
final Context mContext;
- @UnsupportedAppUsage
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ + "LocationManager}")
final ILocationManager mService;
private final Object mLock = new Object();
@@ -421,8 +422,7 @@
try {
return mService.getExtraLocationControllerPackage();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -437,7 +437,7 @@
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -452,7 +452,7 @@
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -466,8 +466,7 @@
try {
return mService.isExtraLocationControllerPackageEnabled();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -485,7 +484,7 @@
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -503,7 +502,7 @@
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -1199,7 +1198,7 @@
mContext.getPackageName(), mContext.getAttributionTag(),
AppOpsManager.toReceiverId(listener));
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -1301,7 +1300,7 @@
// unregistration is complete.
mService.unregisterLocationListener(transport);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -1517,7 +1516,8 @@
Preconditions.checkArgument(command != null, "invalid null command");
try {
- return mService.sendExtraCommand(provider, command, extras);
+ mService.sendExtraCommand(provider, command, extras);
+ return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1835,6 +1835,10 @@
} else {
status.setStatus(gnssStatus, ttff);
}
+ } else if (status == null) {
+ // even though this method is marked as nullable, legacy behavior was to never return
+ // a null result, and there are applications that rely on this behavior.
+ status = GpsStatus.createEmpty();
}
return status;
}
@@ -2424,7 +2428,7 @@
try {
cancellationSignal.cancel();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -2464,7 +2468,8 @@
}
@Override
- public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+ public void onLocationChanged(Location location,
+ @Nullable IRemoteCallback onCompleteCallback) {
executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
@Override
public void operate(LocationListener listener) {
@@ -2473,7 +2478,13 @@
@Override
public void onComplete(boolean success) {
- markComplete(onCompleteCallback);
+ if (onCompleteCallback != null) {
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
});
}
@@ -2488,14 +2499,6 @@
}
});
}
-
- private void markComplete(IRemoteCallback onCompleteCallback) {
- try {
- onCompleteCallback.sendResult(null);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
}
private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 00a4c7e..6e3fb19 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.util.SparseIntArray;
import java.lang.annotation.Retention;
@@ -137,13 +136,15 @@
*/
public static final int TYPE_BUILTIN_SPEAKER_SAFE = 24;
/**
- * @hide
* A device type for rerouting audio within the Android framework between mixes and
- * system applications. Typically created when using
- * {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
- * {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_RENDER} flag.
+ * system applications.
+ * This type is for instance encountered when querying the output device of a track
+ * (with {@link AudioTrack#getRoutedDevice()} playing from a device in screen mirroring mode,
+ * where the audio is not heard on the device, but on the remote device.
*/
- @SystemApi
+ // Typically created when using
+ // {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
+ // {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_LOOP_BACK} flag.
public static final int TYPE_REMOTE_SUBMIX = 25;
/** @hide */
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6d690f0..4a6724a 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1411,9 +1411,9 @@
private static final int IMAGE_TYPE_WEBP = 14;
static {
- sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+ sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.US);
sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
- sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX");
+ sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX", Locale.US);
sFormatterTz.setTimeZone(TimeZone.getTimeZone("UTC"));
// Build up the hash tables to look up Exif tags for reading Exif tags.
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 3c2be5f..029e614 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -25,7 +25,6 @@
import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
import android.media.MediaDescription;
-import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Bundle;
@@ -757,8 +756,8 @@
* Flag: Indicates that the item is playable.
* <p>
* The id of this item may be passed to
- * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
- * to start playing it.
+ * {@link android.media.session.MediaController.TransportControls
+ * #playFromMediaId(String, Bundle)} to start playing it.
* </p>
*/
public static final int FLAG_PLAYABLE = 1 << 1;
@@ -1107,13 +1106,7 @@
}
@Override
- public void onLoadChildren(String parentId, ParceledListSlice list) {
- onLoadChildrenWithOptions(parentId, list, null);
- }
-
- @Override
- public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list,
- final Bundle options) {
+ public void onLoadChildren(String parentId, ParceledListSlice list, Bundle options) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
mediaBrowser.onLoadChildren(this, parentId, list, options);
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 7e3f2f8..a877207 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -23,7 +23,5 @@
void onConnect(String root, in MediaSession.Token session, in Bundle extras);
@UnsupportedAppUsage
void onConnectFailed();
- void onLoadChildren(String mediaId, in ParceledListSlice list);
- void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list,
- in Bundle options);
+ void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 06adf30..39c7682 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -687,7 +687,7 @@
final ParceledListSlice<MediaBrowser.MediaItem> pls =
filteredList == null ? null : new ParceledListSlice<>(filteredList);
try {
- connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
+ connection.callbacks.onLoadChildren(parentId, pls, options);
} catch (RemoteException ex) {
// The other side is in the process of crashing.
Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 6e26f2c..3cd4081 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -337,33 +337,44 @@
}
mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
-
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
- context->mBlock = block;
- std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
- context->mBuffer = pC2Buffer;
- mC2Buffer = pC2Buffer;
- if (mAvHandle->numInts > 0) {
- // use first int in the native_handle as the index
- int index = mAvHandle->data[mAvHandle->numFds];
- std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
- std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
- pC2Buffer->setInfo(info);
+ if (block != nullptr) {
+ // CreateLinearBlock delete mIonHandle after it create block successfully.
+ // ToDo: coordinate who is response to delete mIonHandle
+ mIonHandle = NULL;
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+ context->mBlock = block;
+ std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
+ context->mBuffer = pC2Buffer;
+ mC2Buffer = pC2Buffer;
+ if (mAvHandle->numInts > 0) {
+ // use first int in the native_handle as the index
+ int index = mAvHandle->data[mAvHandle->numFds];
+ std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
+ std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
+ pC2Buffer->setInfo(info);
+ }
+ pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+ jobject linearBlock =
+ env->NewObject(
+ env->FindClass("android/media/MediaCodec$LinearBlock"),
+ gFields.linearBlockInitID);
+ env->CallVoidMethod(
+ linearBlock,
+ gFields.linearBlockSetInternalStateID,
+ (jlong)context.release(),
+ true);
+ mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
+ mAvHandleRefCnt++;
+ return mLinearBlockObj;
+ } else {
+ native_handle_close(const_cast<native_handle_t*>(
+ reinterpret_cast<const native_handle_t*>(mIonHandle)));
+ native_handle_delete(const_cast<native_handle_t*>(
+ reinterpret_cast<const native_handle_t*>(mIonHandle)));
+ mIonHandle = NULL;
+ return NULL;
}
- pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
- jobject linearBlock =
- env->NewObject(
- env->FindClass("android/media/MediaCodec$LinearBlock"),
- gFields.linearBlockInitID);
- env->CallVoidMethod(
- linearBlock,
- gFields.linearBlockSetInternalStateID,
- (jlong)context.release(),
- true);
- mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
- mAvHandleRefCnt++;
- return mLinearBlockObj;
}
uint64_t MediaEvent::getAudioHandle() {
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 0dc7631..2b95992 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -24061,6 +24061,7 @@
field public static final int TYPE_IP = 20; // 0x14
field public static final int TYPE_LINE_ANALOG = 5; // 0x5
field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+ field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
field public static final int TYPE_TELEPHONY = 18; // 0x12
field public static final int TYPE_TV_TUNER = 17; // 0x11
field public static final int TYPE_UNKNOWN = 0; // 0x0
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index a5ca196..c1dbfb7 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -5,6 +5,10 @@
field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
}
+ public class NotificationManager {
+ method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+ }
+
}
package android.content.rollback {
@@ -29,6 +33,10 @@
package android.os {
+ public class Binder implements android.os.IBinder {
+ method public final void markVintfStability();
+ }
+
public class StatsServiceManager {
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/non-updatable-api/removed.txt b/non-updatable-api/removed.txt
index ba05a1b..f2dfb84 100644
--- a/non-updatable-api/removed.txt
+++ b/non-updatable-api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index b41ab93..222e563 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -4112,10 +4112,6 @@
field public static final int ROLE_OUTPUT = 2; // 0x2
}
- public final class AudioDeviceInfo {
- field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
- }
-
public final class AudioFocusInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAttributes();
diff --git a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
index 0a29424..09fbf7a 100644
--- a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
@@ -15,7 +15,6 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:fitsSystemWindows="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
diff --git a/packages/CarSystemUI/res/layout/super_notification_shade.xml b/packages/CarSystemUI/res/layout/super_notification_shade.xml
deleted file mode 100644
index db71c91..0000000
--- a/packages/CarSystemUI/res/layout/super_notification_shade.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the notification shade window. -->
-<com.android.systemui.statusbar.phone.NotificationShadeWindowView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <com.android.systemui.statusbar.BackDropView
- android:id="@+id/backdrop"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- sysui:ignoreRightInset="true"
- >
- <ImageView android:id="@+id/backdrop_back"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"/>
- <ImageView android:id="@+id/backdrop_front"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:visibility="invisible"/>
- </com.android.systemui.statusbar.BackDropView>
-
- <com.android.systemui.statusbar.ScrimView
- android:id="@+id/scrim_behind"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
- <include layout="@layout/brightness_mirror"/>
-
- <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout="@layout/car_fullscreen_user_switcher"/>
-
- <include layout="@layout/notification_center_activity"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginBottom="@dimen/navigation_bar_height"
- android:visibility="invisible"/>
-
- <include layout="@layout/status_bar_expanded"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible"/>
-
- <com.android.systemui.statusbar.ScrimView
- android:id="@+id/scrim_in_front"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
-</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
deleted file mode 100644
index d93f62f..0000000
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the status bar window. -->
-<com.android.systemui.statusbar.phone.StatusBarWindowView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- >
- <FrameLayout
- android:id="@+id/status_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
-
- <FrameLayout
- android:id="@+id/car_top_navigation_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
-</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2dc499c..2c97889 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,12 +22,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- TODO(b/151617493): replace marginBottom with insets. -->
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"
- android:layout_marginBottom="@dimen/navigation_bar_height"/>
+ android:layout="@layout/notification_panel_container"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index cb321cd..8359dac 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -81,6 +81,21 @@
<dimen name="car_keyline_2">96dp</dimen>
<dimen name="car_keyline_3">128dp</dimen>
+ <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
+ <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
+ <!-- Margin between text lines in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
+ <!-- Padding around Ongoing App Ops dialog content -->
+ <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+ <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
+ <dimen name="ongoing_appops_chip_margin">12dp</dimen>
+ <!-- Start and End padding for Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
+ <!-- Padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
+ <!-- Radius of Ongoing App Ops chip corners -->
+ <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+
<!-- Car volume dimens. -->
<dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
<dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index ffdf378..797a178 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -21,8 +21,6 @@
import com.android.systemui.car.navigationbar.CarNavigationBar;
import com.android.systemui.car.notification.CarNotificationModule;
import com.android.systemui.car.sideloaded.SideLoadedAppController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarModule;
import com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier;
import com.android.systemui.car.volume.VolumeUI;
import com.android.systemui.car.window.OverlayWindowModule;
@@ -37,10 +35,10 @@
import com.android.systemui.recents.RecentsModule;
import com.android.systemui.shortcut.ShortcutKeyDispatcher;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.InstantAppNotifier;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
import com.android.systemui.toast.ToastUI;
import com.android.systemui.util.leak.GarbageMonitor;
@@ -51,7 +49,7 @@
import dagger.multibindings.IntoMap;
/** Binder for car specific {@link SystemUI} modules. */
-@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
+@Module(includes = {RecentsModule.class, StatusBarModule.class, NotificationsModule.class,
BubbleModule.class, KeyguardModule.class, OverlayWindowModule.class,
CarNotificationModule.class})
public abstract class CarSystemUIBinder {
@@ -162,19 +160,7 @@
@Binds
@IntoMap
@ClassKey(StatusBar.class)
- public abstract SystemUI bindsStatusBar(CarStatusBar sysui);
-
- /** Inject into TvStatusBar. */
- @Binds
- @IntoMap
- @ClassKey(TvStatusBar.class)
- public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
-
- /** Inject into StatusBarGoogle. */
- @Binds
- @IntoMap
- @ClassKey(CarStatusBar.class)
- public abstract SystemUI bindsCarStatusBar(CarStatusBar sysui);
+ public abstract SystemUI bindsStatusBar(StatusBar sysui);
/** Inject into VolumeUI. */
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index 1a1b93b..03ea941 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -32,7 +32,7 @@
@Override
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
return DaggerCarSystemUIRootComponent.builder()
- .contextHolder(new ContextHolder(context))
+ .context(context)
.build();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index e5fbbd8..7b6dceb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -29,8 +29,6 @@
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
import com.android.systemui.car.keyguard.CarKeyguardViewController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarKeyguardViewManager;
import com.android.systemui.car.statusbar.DozeServiceHost;
import com.android.systemui.car.statusbar.DummyNotificationShadeWindowController;
import com.android.systemui.car.volume.CarVolumeDialogComponent;
@@ -60,8 +58,6 @@
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -132,9 +128,9 @@
@Singleton
@Provides
- static SystemWindows provideSystemWindows(Context context, DisplayController displayController,
+ static SystemWindows provideSystemWindows(DisplayController displayController,
IWindowManager wmService) {
- return new SystemWindows(context, displayController, wmService);
+ return new SystemWindows(displayController, wmService);
}
@Singleton
@@ -183,17 +179,10 @@
CarSystemUIRootComponent systemUIRootComponent);
@Binds
- public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
-
- @Binds
abstract VolumeDialogComponent bindVolumeDialogComponent(
CarVolumeDialogComponent carVolumeDialogComponent);
@Binds
- abstract StatusBarKeyguardViewManager bindStatusBarKeyguardViewManager(
- CarStatusBarKeyguardViewManager keyguardViewManager);
-
- @Binds
abstract KeyguardViewController bindKeyguardViewController(
CarKeyguardViewController carKeyguardViewController);
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 088420e..ece3bee 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -36,12 +36,17 @@
DependencyBinder.class,
PipModule.class,
OneHandedModule.class,
- SystemUIFactory.ContextHolder.class,
SystemServicesModule.class,
SystemUIModule.class,
CarSystemUIModule.class,
CarSystemUIBinder.class
})
public interface CarSystemUIRootComponent extends SystemUIRootComponent {
-
+ /**
+ * Builder for a CarSystemUIRootComponent.
+ */
+ @Component.Builder
+ interface Builder extends SystemUIRootComponent.Builder {
+ CarSystemUIRootComponent build();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 69766cc..51a7245 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -141,6 +141,11 @@
}
@Override
+ protected boolean shouldShowNavigationBar() {
+ return true;
+ }
+
+ @Override
public void onFinishInflate() {
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1eead62..8d58436 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,6 +16,8 @@
package com.android.systemui.car.notification;
+import static android.view.WindowInsets.Type.navigationBars;
+
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -197,6 +199,16 @@
}
@Override
+ protected boolean shouldShowStatusBar() {
+ return true;
+ }
+
+ @Override
+ protected int getInsetTypesToFit() {
+ return navigationBars();
+ }
+
+ @Override
protected boolean shouldShowHUN() {
return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
deleted file mode 100644
index d692487..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
-import com.android.systemui.InitController;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.CarDeviceProvisionedListener;
-import com.android.systemui.car.bluetooth.CarBatteryController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.classifier.FalsingLog;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-
-import dagger.Lazy;
-
-/**
- * A status bar tailored for the automotive use case.
- */
-public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
- private static final String TAG = "CarStatusBar";
-
- private final UserSwitcherController mUserSwitcherController;
- private final ScrimController mScrimController;
-
- private CarBatteryController mCarBatteryController;
- private BatteryMeterView mBatteryMeterView;
- private Drawable mNotificationPanelBackground;
-
- private final Object mQueueLock = new Object();
- private final CarNavigationBarController mCarNavigationBarController;
- private final CarDeviceProvisionedController mCarDeviceProvisionedController;
- private final ScreenLifecycle mScreenLifecycle;
-
- private boolean mDeviceIsSetUpForUser = true;
- private boolean mIsUserSetupInProgress = false;
-
- public CarStatusBar(
- Context context,
- NotificationsController notificationsController,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- BypassHeadsUpNotifier bypassHeadsUpNotifier,
- FalsingManager falsingManager,
- BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
- BubbleController bubbleController,
- NotificationGroupManager groupManager,
- VisualStabilityManager visualStabilityManager,
- CarDeviceProvisionedController carDeviceProvisionedController,
- NavigationBarController navigationBarController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- Optional<Recents> recents,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
- PluginManager pluginManager,
- Optional<Divider> dividerOptional,
- SuperStatusBarViewFactory superStatusBarViewFactory,
- LightsOutNotifController lightsOutNotifController,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- DarkIconDispatcher darkIconDispatcher,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DismissCallbackRegistry dismissCallbackRegistry,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- Lazy<NotificationShadeDepthController> depthControllerLazy,
- /* Car Settings injected components. */
- CarNavigationBarController carNavigationBarController) {
- super(
- context,
- notificationsController,
- lightBarController,
- autoHideController,
- keyguardUpdateMonitor,
- statusBarIconController,
- pulseExpansionHandler,
- notificationWakeUpCoordinator,
- keyguardBypassController,
- keyguardStateController,
- headsUpManagerPhone,
- dynamicPrivacyController,
- bypassHeadsUpNotifier,
- falsingManager,
- broadcastDispatcher,
- remoteInputQuickSettingsDisabler,
- notificationGutsManager,
- notificationLogger,
- notificationInterruptStateProvider,
- notificationViewHierarchyManager,
- keyguardViewMediator,
- displayMetrics,
- metricsLogger,
- uiBgExecutor,
- notificationMediaManager,
- lockScreenUserManager,
- remoteInputManager,
- userSwitcherController,
- networkController,
- batteryController,
- colorExtractor,
- screenLifecycle,
- wakefulnessLifecycle,
- statusBarStateController,
- vibratorHelper,
- bubbleController,
- groupManager,
- visualStabilityManager,
- carDeviceProvisionedController,
- navigationBarController,
- assistManagerLazy,
- configurationController,
- notificationShadeWindowController,
- lockscreenLockIconController,
- dozeParameters,
- scrimController,
- null /* keyguardLiftController */,
- lockscreenWallpaperLazy,
- biometricUnlockControllerLazy,
- dozeServiceHost,
- powerManager,
- screenPinningRequest,
- dozeScrimController,
- volumeComponent,
- commandQueue,
- recents,
- statusBarComponentBuilder,
- pluginManager,
- dividerOptional,
- lightsOutNotifController,
- statusBarNotificationActivityStarterBuilder,
- shadeController,
- superStatusBarViewFactory,
- statusBarKeyguardViewManager,
- viewMediatorCallback,
- initController,
- darkIconDispatcher,
- timeTickHandler,
- pluginDependencyProvider,
- keyguardDismissUtil,
- extensionController,
- userInfoControllerImpl,
- phoneStatusBarPolicy,
- keyguardIndicationController,
- dismissCallbackRegistry,
- depthControllerLazy,
- statusBarTouchableRegionManager);
- mUserSwitcherController = userSwitcherController;
- mScrimController = scrimController;
- mCarDeviceProvisionedController = carDeviceProvisionedController;
- mCarNavigationBarController = carNavigationBarController;
- mScreenLifecycle = screenLifecycle;
- }
-
- @Override
- public void start() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
-
- super.start();
-
- createBatteryController();
- mCarBatteryController.startListening();
-
- mCarDeviceProvisionedController.addCallback(
- new CarDeviceProvisionedListener() {
- @Override
- public void onUserSetupInProgressChanged() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
-
- @Override
- public void onUserSetupChanged() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
-
- @Override
- public void onUserSwitched() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
- });
-
- mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() {
- @Override
- public String getName() {
- return TAG;
- }
-
- @Override
- public boolean suppressInterruptions(NotificationEntry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded.
- // Ensure this by not allowing any interruptions (ie: pinning any notifications) if
- // the shade is already opened.
- return !getPresenter().isPresenterFullyCollapsed();
- }
- });
- }
-
- @Override
- public boolean hideKeyguard() {
- boolean result = super.hideKeyguard();
- mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());
- return result;
- }
-
- @Override
- public void showKeyguard() {
- super.showKeyguard();
- mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser());
- }
-
- private boolean isDeviceSetupForUser() {
- return mDeviceIsSetUpForUser && !mIsUserSetupInProgress;
- }
-
- @Override
- protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
- super.makeStatusBarView(result);
-
- mNotificationPanelBackground = getDefaultWallpaper();
- mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
-
- FragmentHostManager manager = FragmentHostManager.get(mPhoneStatusBarWindow);
- manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
- mBatteryMeterView = fragment.getView().findViewById(R.id.battery);
-
- // By default, the BatteryMeterView should not be visible. It will be toggled
- // when a device has connected by bluetooth.
- mBatteryMeterView.setVisibility(View.GONE);
- });
- }
-
- @Override
- public void animateExpandNotificationsPanel() {
- // No op.
- }
-
- @Override
- protected QS createDefaultQSFragment() {
- return null;
- }
-
- private BatteryController createBatteryController() {
- mCarBatteryController = new CarBatteryController(mContext);
- mCarBatteryController.addBatteryViewHandler(this);
- return mCarBatteryController;
- }
-
- @Override
- protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
- // No op.
- }
-
- @Override
- public void notifyBiometricAuthModeChanged() {
- // No op.
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- //When executing dump() function simultaneously, we need to serialize them
- //to get mStackScroller's position correctly.
- synchronized (mQueueLock) {
- pw.println(" mStackScroller: " + viewInfo(mStackScroller));
- pw.println(" mStackScroller: " + viewInfo(mStackScroller)
- + " scroll " + mStackScroller.getScrollX()
- + "," + mStackScroller.getScrollY());
- }
- pw.print(" mCarBatteryController=");
- pw.println(mCarBatteryController);
- pw.print(" mBatteryMeterView=");
- pw.println(mBatteryMeterView);
-
- if (Dependency.get(KeyguardUpdateMonitor.class) != null) {
- Dependency.get(KeyguardUpdateMonitor.class).dump(fd, pw, args);
- }
-
- FalsingLog.dump(pw);
-
- pw.println("SharedPreferences:");
- for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
- pw.print(" ");
- pw.print(entry.getKey());
- pw.print("=");
- pw.println(entry.getValue());
- }
- }
-
- @Override
- public void showBatteryView() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
- }
-
- if (mBatteryMeterView != null) {
- mBatteryMeterView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void hideBatteryView() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
- }
-
- if (mBatteryMeterView != null) {
- mBatteryMeterView.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void createUserSwitcher() {
- if (!mUserSwitcherController.useFullscreenUserSwitcher()) {
- super.createUserSwitcher();
- }
- }
-
- /**
- * Dismisses the keyguard and shows bouncer if authentication is necessary.
- */
- public void dismissKeyguard() {
- // Don't dismiss keyguard when the screen is off.
- if (mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF) {
- return;
- }
- executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
- true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
- }
-
- /**
- * Ensures that relevant child views are appropriately recreated when the device's density
- * changes.
- */
- @Override
- public void onDensityOrFontScaleChanged() {
- super.onDensityOrFontScaleChanged();
- // Need to update the background on density changed in case the change was due to night
- // mode.
- mNotificationPanelBackground = getDefaultWallpaper();
- mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
- }
-
- /**
- * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
- */
- private Drawable getDefaultWallpaper() {
- return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
deleted file mode 100644
index 96a998a..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.R;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Car implementation of the {@link StatusBarKeyguardViewManager}. */
-@Singleton
-public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
-
- protected boolean mShouldHideNavBar;
- private final CarNavigationBarController mCarNavigationBarController;
- private Set<OnKeyguardCancelClickedListener> mKeygaurdCancelClickedListenerSet;
-
- @Inject
- public CarStatusBarKeyguardViewManager(Context context,
- ViewMediatorCallback callback,
- LockPatternUtils lockPatternUtils,
- SysuiStatusBarStateController sysuiStatusBarStateController,
- ConfigurationController configurationController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- NavigationModeController navigationModeController,
- DockManager dockManager,
- NotificationShadeWindowController notificationShadeWindowController,
- KeyguardStateController keyguardStateController,
- NotificationMediaManager notificationMediaManager,
- CarNavigationBarController carNavigationBarController) {
- super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
- configurationController, keyguardUpdateMonitor, navigationModeController,
- dockManager, notificationShadeWindowController, keyguardStateController,
- notificationMediaManager);
- mShouldHideNavBar = context.getResources()
- .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
- mCarNavigationBarController = carNavigationBarController;
- mKeygaurdCancelClickedListenerSet = new HashSet<>();
- }
-
- @Override
- protected void updateNavigationBarVisibility(boolean navBarVisible) {
- if (!mShouldHideNavBar) {
- return;
- }
- int visibility = navBarVisible ? View.VISIBLE : View.GONE;
- mCarNavigationBarController.setBottomWindowVisibility(visibility);
- mCarNavigationBarController.setLeftWindowVisibility(visibility);
- mCarNavigationBarController.setRightWindowVisibility(visibility);
- }
-
- /**
- * Car is a multi-user system. There's a cancel button on the bouncer that allows the user to
- * go back to the user switcher and select another user. Different user may have different
- * security mode which requires bouncer container to be resized. For this reason, the bouncer
- * view is destroyed on cancel.
- */
- @Override
- protected boolean shouldDestroyViewOnReset() {
- return true;
- }
-
- /**
- * Called when cancel button in bouncer is pressed.
- */
- @Override
- public void onCancelClicked() {
- mKeygaurdCancelClickedListenerSet.forEach(OnKeyguardCancelClickedListener::onCancelClicked);
- }
-
- /**
- * Do nothing on this change.
- * The base class hides the keyguard which for automotive we want to avoid b/c this would happen
- * on a configuration change due to day/night (headlight state).
- */
- @Override
- public void onDensityOrFontScaleChanged() { }
-
- /**
- * Add listener for keyguard cancel clicked.
- */
- public void addOnKeyguardCancelClickedListener(
- OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
- mKeygaurdCancelClickedListenerSet.add(keyguardCancelClickedListener);
- }
-
- /**
- * Remove listener for keyguard cancel clicked.
- */
- public void removeOnKeyguardCancelClickedListener(
- OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
- mKeygaurdCancelClickedListenerSet.remove(keyguardCancelClickedListener);
- }
-
-
- /**
- * Defines a callback for keyguard cancel button clicked listeners.
- */
- public interface OnKeyguardCancelClickedListener {
- /**
- * Called when keyguard cancel button is clicked.
- */
- void onCancelClicked();
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
deleted file mode 100644
index dc2eb04..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.InitController;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneDependenciesModule;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-import dagger.Lazy;
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Dagger Module providing {@link CarStatusBar}.
- */
-@Module(includes = {StatusBarDependenciesModule.class, StatusBarPhoneDependenciesModule.class,
- NotificationRowModule.class})
-public class CarStatusBarModule {
- /**
- * Provides our instance of StatusBar which is considered optional.
- */
- @Provides
- @Singleton
- static CarStatusBar provideStatusBar(
- Context context,
- NotificationsController notificationsController,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- BypassHeadsUpNotifier bypassHeadsUpNotifier,
- FalsingManager falsingManager,
- BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptionStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
- BubbleController bubbleController,
- NotificationGroupManager groupManager,
- VisualStabilityManager visualStabilityManager,
- CarDeviceProvisionedController carDeviceProvisionedController,
- NavigationBarController navigationBarController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- Optional<Recents> recentsOptional,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
- PluginManager pluginManager,
- Optional<Divider> dividerOptional,
- SuperStatusBarViewFactory superStatusBarViewFactory,
- LightsOutNotifController lightsOutNotifController,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- DarkIconDispatcher darkIconDispatcher,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DismissCallbackRegistry dismissCallbackRegistry,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
- CarNavigationBarController carNavigationBarController) {
- return new CarStatusBar(
- context,
- notificationsController,
- lightBarController,
- autoHideController,
- keyguardUpdateMonitor,
- statusBarIconController,
- pulseExpansionHandler,
- notificationWakeUpCoordinator,
- keyguardBypassController,
- keyguardStateController,
- headsUpManagerPhone,
- dynamicPrivacyController,
- bypassHeadsUpNotifier,
- falsingManager,
- broadcastDispatcher,
- remoteInputQuickSettingsDisabler,
- notificationGutsManager,
- notificationLogger,
- notificationInterruptionStateProvider,
- notificationViewHierarchyManager,
- keyguardViewMediator,
- displayMetrics,
- metricsLogger,
- uiBgExecutor,
- notificationMediaManager,
- lockScreenUserManager,
- remoteInputManager,
- userSwitcherController,
- networkController,
- batteryController,
- colorExtractor,
- screenLifecycle,
- wakefulnessLifecycle,
- statusBarStateController,
- vibratorHelper,
- bubbleController,
- groupManager,
- visualStabilityManager,
- carDeviceProvisionedController,
- navigationBarController,
- assistManagerLazy,
- configurationController,
- notificationShadeWindowController,
- lockscreenLockIconController,
- dozeParameters,
- scrimController,
- lockscreenWallpaperLazy,
- biometricUnlockControllerLazy,
- dozeServiceHost,
- powerManager,
- screenPinningRequest,
- dozeScrimController,
- volumeComponent,
- commandQueue,
- recentsOptional,
- statusBarComponentBuilder,
- pluginManager,
- dividerOptional,
- superStatusBarViewFactory,
- lightsOutNotifController,
- statusBarNotificationActivityStarterBuilder,
- shadeController,
- statusBarKeyguardViewManager,
- viewMediatorCallback,
- initController,
- darkIconDispatcher,
- timeTickHandler,
- pluginDependencyProvider,
- keyguardDismissUtil,
- extensionController,
- userInfoControllerImpl,
- phoneStatusBarPolicy,
- keyguardIndicationController,
- dismissCallbackRegistry,
- statusBarTouchableRegionManager,
- notificationShadeDepthControllerLazy,
- carNavigationBarController);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 45f3d34..0d77c13 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -91,6 +91,11 @@
R.integer.config_userSwitchTransitionViewShownTimeoutMs);
}
+ @Override
+ protected int getInsetTypesToFit() {
+ return 0;
+ }
+
/**
* Makes the user switch transition view appear and draws the content inside of it if a user
* that is different from the previous user is provided and if the dialog is not already
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 3969f92..53deb9d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -16,9 +16,12 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.statusBars;
+
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
/**
* Owns a {@link View} that is present in SystemUIOverlayWindow.
@@ -140,9 +143,25 @@
}
/**
+ * Returns {@code true} if status bar should be displayed over this view.
+ */
+ protected boolean shouldShowStatusBar() {
+ return false;
+ }
+
+ /**
* Returns {@code true} if this view should be hidden during the occluded state.
*/
protected boolean shouldShowWhenOccluded() {
return false;
}
+
+ /**
+ * Returns the insets types to fit to the sysui overlay window when this
+ * {@link OverlayViewController} is in the foreground.
+ */
+ @WindowInsets.Type.InsetsType
+ protected int getInsetTypesToFit() {
+ return statusBars();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 8e94109..2494242 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,13 +16,17 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
import android.annotation.Nullable;
import android.util.Log;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsController;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -48,10 +52,7 @@
private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
- private final CarNavigationBarController mCarNavigationBarController;
-
- private boolean mIsOccluded;
-
+ private final WindowInsetsController mWindowInsetsController;
@VisibleForTesting
Map<OverlayViewController, Integer> mZOrderMap;
@VisibleForTesting
@@ -60,14 +61,15 @@
Set<OverlayViewController> mViewsHiddenForOcclusion;
@VisibleForTesting
OverlayViewController mHighestZOrder;
+ private boolean mIsOccluded;
@Inject
public OverlayViewGlobalStateController(
- CarNavigationBarController carNavigationBarController,
SystemUIOverlayWindowController systemUIOverlayWindowController) {
mSystemUIOverlayWindowController = systemUIOverlayWindowController;
mSystemUIOverlayWindowController.attach();
- mCarNavigationBarController = carNavigationBarController;
+ mWindowInsetsController =
+ mSystemUIOverlayWindowController.getBaseLayout().getWindowInsetsController();
mZOrderMap = new HashMap<>();
mZOrderVisibleSortedMap = new TreeMap<>();
mViewsHiddenForOcclusion = new HashSet<>();
@@ -115,7 +117,9 @@
}
updateInternalsWhenShowingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
Log.d(TAG, "Content shown: " + viewController.getClass().getName());
debugLog();
@@ -185,7 +189,9 @@
mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
refreshHighestZOrderWhenHidingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(false);
@@ -208,10 +214,28 @@
}
private void refreshNavigationBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
- mCarNavigationBarController.showBars();
+ mWindowInsetsController.show(navigationBars());
} else {
- mCarNavigationBarController.hideBars();
+ mWindowInsetsController.hide(navigationBars());
+ }
+ }
+
+ private void refreshStatusBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+ if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) {
+ mWindowInsetsController.show(statusBars());
+ } else {
+ mWindowInsetsController.hide(statusBars());
+ }
+ }
+
+ private void refreshInsetTypesToFit() {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
+ setFitInsetsTypes(statusBars());
+ } else {
+ setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
}
}
@@ -224,6 +248,10 @@
mSystemUIOverlayWindowController.setWindowVisible(visible);
}
+ private void setFitInsetsTypes(@InsetsType int types) {
+ mSystemUIOverlayWindowController.setFitInsetsTypes(types);
+ }
+
/**
* Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
* sysui overlay window.
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index bcd96f6..029bd37 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -25,6 +25,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
import com.android.systemui.R;
@@ -99,7 +100,6 @@
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(/* types= */ 0);
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("SystemUIOverlayWindow");
mLp.packageName = mContext.getPackageName();
@@ -110,6 +110,12 @@
setWindowVisible(false);
}
+ /** Sets the types of insets to fit. Note: This should be rarely used. */
+ public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
+ mLpChanged.setFitInsetsTypes(types);
+ updateWindow();
+ }
+
/** Sets the window to the visible state. */
public void setWindowVisible(boolean visible) {
mVisible = visible;
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index 63f8c72..5c80202 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -16,12 +16,14 @@
package com.android.systemui.wm;
+import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
import android.view.InsetsController;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -32,7 +34,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import javax.inject.Inject;
@@ -50,29 +51,32 @@
private static final String TAG = "DisplaySystemBarsController";
private SparseArray<PerDisplay> mPerDisplaySparseArray;
+ private final Context mContext;
@Inject
public DisplaySystemBarsController(
- SystemWindows syswin,
+ Context context,
+ IWindowManager wmService,
DisplayController displayController,
@Main Handler mainHandler,
TransactionPool transactionPool) {
- super(syswin, displayController, mainHandler, transactionPool);
+ super(wmService, displayController, mainHandler, transactionPool);
+ mContext = context;
}
@Override
public void onDisplayAdded(int displayId) {
PerDisplay pd = new PerDisplay(displayId);
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
// Lazy loading policy control filters instead of during boot.
if (mPerDisplaySparseArray == null) {
mPerDisplaySparseArray = new SparseArray<>();
- BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
- BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
+ BarControlPolicy.reloadFromSetting(mContext);
+ BarControlPolicy.registerContentObserver(mContext, mHandler, () -> {
int size = mPerDisplaySparseArray.size();
for (int i = 0; i < size; i++) {
mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
@@ -85,7 +89,7 @@
@Override
public void onDisplayRemoved(int displayId) {
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -155,7 +159,7 @@
showInsets(barVisibilities[0], /* fromIme= */ false);
hideInsets(barVisibilities[1], /* fromIme= */ false);
try {
- mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to update window manager service.");
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 20f9bc8e..ff28665 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -16,9 +16,14 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,19 +33,18 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarSystemUiTest;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
@@ -58,8 +62,6 @@
private ViewGroup mBaseLayout;
@Mock
- private CarNavigationBarController mCarNavigationBarController;
- @Mock
private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
@Mock
private OverlayViewMediator mOverlayViewMediator;
@@ -71,18 +73,22 @@
private OverlayPanelViewController mOverlayPanelViewController;
@Mock
private Runnable mRunnable;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
- mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+ mBaseLayout = spy((ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_global_state_controller_test, /* root= */ null));
+
+ when(mBaseLayout.getWindowInsetsController()).thenReturn(mWindowInsetsController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
- mCarNavigationBarController, mSystemUIOverlayWindowController);
+ mSystemUIOverlayWindowController);
verify(mSystemUIOverlayWindowController).attach();
}
@@ -108,7 +114,7 @@
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -118,7 +124,37 @@
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -168,10 +204,11 @@
setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -183,7 +220,46 @@
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -216,10 +292,11 @@
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -231,7 +308,44 @@
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -402,10 +516,11 @@
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -418,7 +533,48 @@
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(statusBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -441,10 +597,11 @@
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -457,7 +614,48 @@
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -479,7 +677,27 @@
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_insetsAdjustedToDefault() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(statusBars());
}
@Test
@@ -615,7 +833,7 @@
private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
- Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+ reset(mSystemUIOverlayWindowController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
index 0f28d38..391f75e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -34,7 +34,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
@@ -53,8 +52,6 @@
private static final int DISPLAY_ID = 1;
@Mock
- private SystemWindows mSystemWindows;
- @Mock
private IWindowManager mIWindowManager;
@Mock
private DisplayController mDisplayController;
@@ -66,11 +63,10 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mSystemWindows.mContext = mContext;
- mSystemWindows.mWmService = mIWindowManager;
mController = new DisplaySystemBarsController(
- mSystemWindows,
+ mContext,
+ mIWindowManager,
mDisplayController,
mHandler,
mTransactionPool
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f..02f4457 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -29,6 +29,7 @@
import android.util.Log;
import android.view.Gravity;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -75,6 +76,14 @@
mDeviceListView = findViewById(R.id.device_list);
final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
mDeviceListView.setAdapter(adapter);
+ mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
+ getService().mSelectedDevice =
+ (DeviceFilterPair) adapterView.getItemAtPosition(pos);
+ adapter.notifyDataSetChanged();
+ }
+ });
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
@@ -157,4 +166,4 @@
new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
finish();
}
-}
\ No newline at end of file
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 7aa997e..bcaee36 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -349,10 +349,6 @@
? WIFI_ICON
: BLUETOOTH_ICON,
null, null, null);
- textView.setOnClickListener((view) -> {
- mSelectedDevice = device;
- notifyDataSetChanged();
- });
}
//TODO move to a layout file
diff --git a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
index fdb620b..019c0c9 100644
--- a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- testFromJsonString -->
- <string name="blacklist_json_string" translatable="false">
+ <string name="blocklist_json_string" translatable="false">
{
\"entries\":[
{
diff --git a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
index 82ce542..c1233eb 100644
--- a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
+++ b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
@@ -47,32 +47,32 @@
private static Context sContext;
- private static String sBlacklistJsonString;
+ private static String sBlocklistJsonString;
@BeforeClass
public static void setUpClass() throws Exception {
sContext = InstrumentationRegistry.getInstrumentation().getContext();
- sBlacklistJsonString =
- sContext.getString(com.android.dynsystem.tests.R.string.blacklist_json_string);
+ sBlocklistJsonString =
+ sContext.getString(com.android.dynsystem.tests.R.string.blocklist_json_string);
}
@Test
@SmallTest
public void testFromJsonString() throws JSONException {
- KeyRevocationList blacklist;
- blacklist = KeyRevocationList.fromJsonString(sBlacklistJsonString);
- Assert.assertNotNull(blacklist);
- Assert.assertFalse(blacklist.mEntries.isEmpty());
- blacklist = KeyRevocationList.fromJsonString("{}");
- Assert.assertNotNull(blacklist);
- Assert.assertTrue(blacklist.mEntries.isEmpty());
+ KeyRevocationList blocklist;
+ blocklist = KeyRevocationList.fromJsonString(sBlocklistJsonString);
+ Assert.assertNotNull(blocklist);
+ Assert.assertFalse(blocklist.mEntries.isEmpty());
+ blocklist = KeyRevocationList.fromJsonString("{}");
+ Assert.assertNotNull(blocklist);
+ Assert.assertTrue(blocklist.mEntries.isEmpty());
}
@Test
@SmallTest
public void testFromUrl() throws IOException, JSONException {
URLConnection mockConnection = mock(URLConnection.class);
- doReturn(new ByteArrayInputStream(sBlacklistJsonString.getBytes()))
+ doReturn(new ByteArrayInputStream(sBlocklistJsonString.getBytes()))
.when(mockConnection).getInputStream();
URL mockUrl = new URL(
"http", // protocol
@@ -97,36 +97,36 @@
}
});
- KeyRevocationList blacklist = KeyRevocationList.fromUrl(mockUrl);
- Assert.assertNotNull(blacklist);
- Assert.assertFalse(blacklist.mEntries.isEmpty());
+ KeyRevocationList blocklist = KeyRevocationList.fromUrl(mockUrl);
+ Assert.assertNotNull(blocklist);
+ Assert.assertFalse(blocklist.mEntries.isEmpty());
- blacklist = null;
+ blocklist = null;
try {
- blacklist = KeyRevocationList.fromUrl(mockBadUrl);
+ blocklist = KeyRevocationList.fromUrl(mockBadUrl);
// Up should throw, down should be unreachable
Assert.fail("Expected IOException not thrown");
} catch (IOException e) {
// This is expected, do nothing
}
- Assert.assertNull(blacklist);
+ Assert.assertNull(blocklist);
}
@Test
@SmallTest
public void testIsRevoked() {
- KeyRevocationList blacklist = new KeyRevocationList();
- blacklist.addEntry("key1", "REVOKED", "reason for key1");
+ KeyRevocationList blocklist = new KeyRevocationList();
+ blocklist.addEntry("key1", "REVOKED", "reason for key1");
KeyRevocationList.RevocationStatus revocationStatus =
- blacklist.getRevocationStatusForKey("key1");
+ blocklist.getRevocationStatusForKey("key1");
Assert.assertNotNull(revocationStatus);
Assert.assertEquals(revocationStatus.mReason, "reason for key1");
- revocationStatus = blacklist.getRevocationStatusForKey("key2");
+ revocationStatus = blocklist.getRevocationStatusForKey("key2");
Assert.assertNull(revocationStatus);
- Assert.assertTrue(blacklist.isRevoked("key1"));
- Assert.assertFalse(blacklist.isRevoked("key2"));
+ Assert.assertTrue(blocklist.isRevoked("key1"));
+ Assert.assertFalse(blocklist.isRevoked("key2"));
}
}
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index d480ff6..508cbfc 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -140,8 +140,8 @@
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
- <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Aplicações removidas"</string>
- <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Aplicações e utilizadores removidos"</string>
+ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Apps removidas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Apps e utilizadores removidos"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Atualizações do sistema"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"Ligação USB"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hotspot portátil"</string>
@@ -365,7 +365,7 @@
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração de animação"</string>
<string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular apresentações secundárias"</string>
- <string name="debug_applications_category" msgid="5394089406638954196">"Aplicações"</string>
+ <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
<string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
<string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruir atividades assim que o utilizador sair"</string>
<string name="app_process_limit_title" msgid="8361367869453043007">"Limite do processo em 2º plano"</string>
@@ -396,7 +396,7 @@
<item msgid="4548987861791236754">"Cores naturais e realistas"</item>
<item msgid="1282170165150762976">"Cores otimizadas para conteúdos digitais"</item>
</string-array>
- <string name="inactive_apps_title" msgid="5372523625297212320">"Aplicações em espera"</string>
+ <string name="inactive_apps_title" msgid="5372523625297212320">"Apps em espera"</string>
<string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Toque para ativar/desativar."</string>
<string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Toque para ativar/desativar."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Estado do Modo de espera das apps:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 2f1191a..6914f0a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -55,11 +55,11 @@
"hybrid_sysui_battery_warning_flags";
/**
- * The following blacklists contain settings that should *not* be backed up and restored to
+ * The following denylists contain settings that should *not* be backed up and restored to
* another device. As a general rule, anything that is not user configurable should be
- * blacklisted (and conversely, things that *are* user configurable *should* be backed up)
+ * denied (and conversely, things that *are* user configurable *should* be backed up)
*/
- private static final Set<String> BACKUP_BLACKLISTED_SYSTEM_SETTINGS =
+ private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
newHashSet(
Settings.System.ADVANCED_SETTINGS, // candidate for backup?
Settings.System.ALARM_ALERT_CACHE, // internal cache
@@ -105,7 +105,7 @@
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
);
- private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
+ private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
newHashSet(
Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED,
@@ -590,7 +590,7 @@
Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
- private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
+ private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O.
@@ -745,42 +745,42 @@
Settings.Secure.SUPPRESS_DOZE);
@Test
- public void systemSettingsBackedUpOrBlacklisted() {
- checkSettingsBackedUpOrBlacklisted(
+ public void systemSettingsBackedUpOrDenied() {
+ checkSettingsBackedUpOrDenied(
getCandidateSettings(Settings.System.class),
newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
- BACKUP_BLACKLISTED_SYSTEM_SETTINGS);
+ BACKUP_DENY_LIST_SYSTEM_SETTINGS);
}
@Test
- public void globalSettingsBackedUpOrBlacklisted() {
- checkSettingsBackedUpOrBlacklisted(
+ public void globalSettingsBackedUpOrDenied() {
+ checkSettingsBackedUpOrDenied(
getCandidateSettings(Settings.Global.class),
newHashSet(GlobalSettings.SETTINGS_TO_BACKUP),
- BACKUP_BLACKLISTED_GLOBAL_SETTINGS);
+ BACKUP_DENY_LIST_GLOBAL_SETTINGS);
}
@Test
@Suppress //("b/148236308")
- public void secureSettingsBackedUpOrBlacklisted() {
+ public void secureSettingsBackedUpOrDenied() {
HashSet<String> keys = new HashSet<String>();
Collections.addAll(keys, SecureSettings.SETTINGS_TO_BACKUP);
Collections.addAll(keys, DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
- checkSettingsBackedUpOrBlacklisted(
+ checkSettingsBackedUpOrDenied(
getCandidateSettings(Settings.Secure.class),
keys,
- BACKUP_BLACKLISTED_SECURE_SETTINGS);
+ BACKUP_DENY_LIST_SECURE_SETTINGS);
}
- private static void checkSettingsBackedUpOrBlacklisted(
- Set<String> settings, Set<String> settingsToBackup, Set<String> blacklist) {
+ private static void checkSettingsBackedUpOrDenied(
+ Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
- Set<String> settingsNotBackedUpOrBlacklisted = difference(settingsNotBackedUp, blacklist);
- assertWithMessage("Settings not backed up or blacklisted")
- .that(settingsNotBackedUpOrBlacklisted).isEmpty();
+ Set<String> settingsNotBackedUpOrDenied = difference(settingsNotBackedUp, denylist);
+ assertWithMessage("Settings not backed up or denied")
+ .that(settingsNotBackedUpOrDenied).isEmpty();
- assertWithMessage("blacklisted settings backed up")
- .that(intersect(settingsToBackup, blacklist)).isEmpty();
+ assertWithMessage("denied settings backed up")
+ .that(intersect(settingsToBackup, denylist)).isEmpty();
}
private static Set<String> getCandidateSettings(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6aa233b..98d3553 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -239,6 +239,7 @@
<!-- Listen app op changes -->
<uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<!-- to read and change hvac values in a car -->
<uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
@@ -395,19 +396,15 @@
<!-- Springboard for launching the share and edit activity. This needs to be in the main
system ui process since we need to notify the status bar to dismiss the keyguard -->
- <receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
- android:exported="false" />
-
- <!-- Callback for dismissing screenshot notification after a share target is picked -->
- <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
+ <receiver android:name=".screenshot.ActionProxyReceiver"
android:exported="false" />
<!-- Callback for deleting screenshot notification -->
- <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
+ <receiver android:name=".screenshot.DeleteScreenshotReceiver"
android:exported="false" />
<!-- Callback for invoking a smart action from the screenshot notification. -->
- <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
+ <receiver android:name=".screenshot.SmartActionsReceiver"
android:exported="false"/>
<!-- started from UsbDeviceSettingsManager -->
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index bb68647..8917013 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -41,8 +41,6 @@
The root component is composed of root modules, which in turn provide the global singleton
dependencies across all of SystemUI.
-- `ContextHolder` is just a wrapper that provides a context.
-
- `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
variants (like other form factors e.g. Car).
@@ -52,41 +50,8 @@
### Adding injection to a new SystemUI object
-Anything that depends on any `@Singleton` provider from SystemUIRootComponent
-should be declared as a `@Subcomponent` of the root component. This requires
-declaring your own interface for generating your own modules or just the
-object you need injected. The subcomponent also needs to be added to
-SystemUIRootComponent in SystemUIFactory so it can be acquired.
-
-```java
-public interface SystemUIRootComponent {
-+ @Singleton
-+ Dependency.DependencyInjector createDependency();
-}
-
-public class Dependency extends SystemUI {
- //...
-+ @Subcomponent
-+ public interface DependencyInjector {
-+ Dependency createSystemUI();
-+ }
-}
-```
-
-For objects which extend SystemUI and require injection, you can define an
-injector that creates the injected object for you. This other class should
-be referenced in [@string/config_systemUIServiceComponents](packages/SystemUI/res/values/config.xml).
-
-```java
-public static class DependencyCreator implements Injector {
- @Override
- public SystemUI apply(Context context) {
- return SystemUIFactory.getInstance().getRootComponent()
- .createDependency()
- .createSystemUI();
- }
-}
-```
+SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses
+information in that file to locate and construct an instance of the requested SystemUI class.
### Adding a new injectable object
@@ -147,7 +112,7 @@
```java
public interface FragmentCreator {
-+ NavigationBarFragment createNavigationBar();
+ NavigationBarFragment createNavigationBar();
}
```
@@ -160,49 +125,17 @@
### Using injection with Views
-Generally, you shouldn't need to inject for a view, as the view should
-be relatively self contained and logic that requires injection should be
-moved to a higher level construct such as a Fragment or a top-level SystemUI
-component, see above for how to do injection for both of which.
+DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
-Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
-lot of code that could benefit from injection and will need to be migrated
-off from Dependency#get uses. Similar to how fragments are injected, the view
-needs to be added to the interface
-com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+Needing to inject objects into your View's constructor generally implies you
+are doing more work in your presentation layer than is advisable.
+Instead, create an injected controller for you view, inject into the
+controller, and then attach the view to the controller after inflation.
-```java
-public interface ViewInstanceCreator {
-+ QuickStatusBarHeader createQsHeader();
-}
-```
-
-Presumably you need to inflate that view from XML (otherwise why do you
-need anything special? see earlier sections about generic injection). To obtain
-an inflater that supports injected objects, call InjectionInflationController#injectable,
-which will wrap the inflater it is passed in one that can create injected
-objects when needed.
-
-```java
-@Override
-public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
- Bundle savedInstanceState) {
- return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
-}
-```
-
-There is one other important thing to note about injecting with views. SysUI
-already has a Context in its global dagger component, so if you simply inject
-a Context, you will not get the one that the view should have with proper
-theming. Because of this, always ensure to tag views that have @Inject with
-the @Named view context.
-
-```java
-public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
- OtherCustomDependency something) {
- //...
-}
-```
+View injection generally causes headaches while testing, as inflating a view
+(which may in turn inflate other views) implicitly causes a Dagger graph to
+be stood up, which may or may not contain the appropriately
+faked/mocked/stubbed objects. It is a hard to control process.
## Updating Dagger2
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
new file mode 100644
index 0000000..827cf4a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#242424" /> <!-- 14% of white -->
+ <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
+ android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
+ <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 87dd58e..213bb92 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.bubbles.BubbleManageEducationView
+<com.android.systemui.bubbles.ManageEducationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -87,4 +87,4 @@
/>
</LinearLayout>
</LinearLayout>
-</com.android.systemui.bubbles.BubbleManageEducationView>
+</com.android.systemui.bubbles.ManageEducationView>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
new file mode 100644
index 0000000..3c30632
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<com.android.systemui.privacy.OngoingPrivacyChip
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/privacy_chip"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ android:focusable="true" >
+
+ <FrameLayout
+ android:id="@+id/background"
+ android:layout_height="@dimen/ongoing_appops_chip_height"
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_gravity="center_vertical">
+ <LinearLayout
+ android:id="@+id/icons_container"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical"
+ />
+ </FrameLayout>
+</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index be86e5f..3c74801 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -14,7 +14,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
-->
-<FrameLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_status_bar_system_icons"
@@ -27,6 +27,13 @@
android:clickable="true"
android:paddingTop="@dimen/status_bar_padding_top" >
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start" >
+
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
@@ -38,5 +45,23 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
systemui:showDark="false" />
+ </LinearLayout>
-</FrameLayout>
+ <android.widget.Space
+ android:id="@+id/space"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|end" >
+
+ <include layout="@layout/ongoing_privacy_chip" />
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 27db8cb..f407a8d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -499,6 +499,8 @@
<item>com.android.systemui</item>
</string-array>
+ <integer name="ongoing_appops_dialog_max_apps">5</integer>
+
<!-- Launcher package name for overlaying icons. -->
<string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ea855eb..122fcb2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1137,6 +1137,23 @@
<!-- How much into a DisplayCutout's bounds we can go, on each side -->
<dimen name="display_cutout_margin_consumption">0px</dimen>
+
+ <!-- Height of the Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_height">32dp</dimen>
+ <!-- Padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen>
+ <!-- Side padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+ <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip when QS-->
+ <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+ <!-- Icon size of Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
+ <!-- Radius of Ongoing App Ops chip corners -->
+ <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
+
+
<!-- How much each bubble is elevated. -->
<dimen name="bubble_elevation">1dp</dimen>
<!-- How much the bubble flyout text container is elevated. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d97aea7..8b6543a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2596,6 +2596,27 @@
app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
+ <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
+
+ <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
+
+ <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_separator">,\u0020</string>
+
+ <!-- Separator for types, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_last_separator">\u0020and\u0020</string>
+
+ <!-- Text for camera app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_camera">camera</string>
+
+ <!-- Text for location app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_location">location</string>
+
+ <!-- Text for microphone app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_microphone">microphone</string>
+
<!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
<string name="sensor_privacy_mode">Sensors off</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8a36e7b..878947f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -287,11 +287,11 @@
private final Executor mBackgroundExecutor;
/**
- * Short delay before restarting biometric authentication after a successful try
- * This should be slightly longer than the time between on<biometric>Authenticated
- * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
+ * Short delay before restarting fingerprint authentication after a successful try. This should
+ * be slightly longer than the time between onFingerprintAuthenticated and
+ * setKeyguardGoingAway(true).
*/
- private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
+ private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
// If the HAL dies or is unable to authenticate, keyguard should retry after a short delay
private int mHardwareFingerprintUnavailableRetryCount = 0;
@@ -599,7 +599,7 @@
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
- BIOMETRIC_CONTINUE_DELAY_MS);
+ FINGERPRINT_CONTINUE_DELAY_MS);
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
@@ -782,9 +782,6 @@
}
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
- BIOMETRIC_CONTINUE_DELAY_MS);
-
// Only authenticate face once when assistant is visible
mAssistantVisible = false;
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 49a2328..58f8c07 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -54,6 +54,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.RecordingController;
@@ -130,9 +131,9 @@
import javax.inject.Inject;
import javax.inject.Named;
+import javax.inject.Singleton;
import dagger.Lazy;
-import dagger.Subcomponent;
/**
* Class to handle ugly dependencies throughout sysui until we determine the
@@ -149,6 +150,7 @@
* they have no clients they should not have any registered resources like bound
* services, registered receivers, etc.
*/
+@Singleton
public class Dependency {
/**
* Key for getting a the main looper.
@@ -294,6 +296,7 @@
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
+ @Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Background Lazy<Looper> mBgLooper;
@Inject @Background Lazy<Handler> mBgHandler;
@Inject @Main Lazy<Looper> mMainLooper;
@@ -491,6 +494,7 @@
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
mProviders.put(ClockManager.class, mClockManager::get);
+ mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
@@ -519,7 +523,12 @@
mProviders.put(RecordingController.class, mRecordingController::get);
mProviders.put(Divider.class, mDivider::get);
- sDependency = this;
+ Dependency.setInstance(this);
+ }
+
+ @VisibleForTesting
+ public static void setInstance(Dependency dependency) {
+ sDependency = dependency;
}
protected final <T> T getDependency(Class<T> cls) {
@@ -546,7 +555,7 @@
}
@VisibleForTesting
- protected <T> T createDependency(Object cls) {
+ public <T> T createDependency(Object cls) {
Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
@SuppressWarnings("unchecked")
@@ -635,9 +644,4 @@
return mDisplayName;
}
}
-
- @Subcomponent
- public interface DependencyInjector {
- void createSystemUI(Dependency dependency);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 5674fdd..1a15c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,7 +16,6 @@
package com.android.systemui;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
@@ -30,7 +29,6 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.DaggerSystemUIRootComponent;
-import com.android.systemui.dagger.DependencyProvider;
import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
@@ -48,9 +46,6 @@
import java.util.concurrent.Executor;
-import dagger.Module;
-import dagger.Provides;
-
/**
* Class factory to provide customizable SystemUI components.
*/
@@ -97,24 +92,13 @@
// Every other part of our codebase currently relies on Dependency, so we
// really need to ensure the Dependency gets initialized early on.
-
- Dependency dependency = new Dependency();
- mRootComponent.createDependency().createSystemUI(dependency);
+ Dependency dependency = mRootComponent.createDependency();
dependency.start();
}
- protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) {
- if (mRootComponent != null) {
- throw new RuntimeException("Root component can be set only once.");
- }
-
- mRootComponent = rootComponent;
- }
-
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
return DaggerSystemUIRootComponent.builder()
- .dependencyProvider(new DependencyProvider())
- .contextHolder(new ContextHolder(context))
+ .context(context)
.build();
}
@@ -168,18 +152,4 @@
Dependency.get(DozeParameters.class),
Dependency.get(BubbleController.class));
}
-
- @Module
- public static class ContextHolder {
- private Context mContext;
-
- public ContextHolder(Context context) {
- mContext = context;
- }
-
- @Provides
- public Context provideContext() {
- return mContext;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
new file mode 100644
index 0000000..769a344
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Supplies the instance with given display Id. It generates a new instance if the corresponding
+ * one is not existed. It should run in single thread to avoid race conditions.
+ *
+ * @param <T> the type of results supplied by {@link #createInstance(Display)}.
+ */
+abstract class DisplayIdIndexSupplier<T> {
+
+ private final SparseArray<T> mSparseArray = new SparseArray<>();
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * @param displayManager DisplayManager
+ */
+ DisplayIdIndexSupplier(DisplayManager displayManager) {
+ mDisplayManager = displayManager;
+ }
+
+ /**
+ * @param displayId the logical display Id
+ * @return {@code null} if the given display id is invalid
+ */
+ @Nullable
+ public T get(int displayId) {
+ T instance = mSparseArray.get(displayId);
+ if (instance != null) {
+ return instance;
+ }
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ return null;
+ }
+ instance = createInstance(display);
+ mSparseArray.put(displayId, instance);
+ return instance;
+ }
+
+ @NonNull
+ protected abstract T createInstance(Display display);
+
+ /**
+ * Removes the instance with given display Id.
+ *
+ * @param displayId the logical display id
+ */
+ public void remove(int displayId) {
+ mSparseArray.remove(displayId);
+ }
+
+ /**
+ * Clears all elements.
+ */
+ public void clear() {
+ mSparseArray.clear();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 398a2c9..68a0a65 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -30,6 +30,7 @@
/**
* Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
* {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * The button UI would automatically be dismissed after displaying for a period of time.
*/
class MagnificationModeSwitch {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index e73ff13..ffc70bc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -19,31 +19,33 @@
import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
-import android.util.Log;
-import android.util.SparseArray;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+
import javax.inject.Singleton;
/**
- * Class to control magnification mode switch button. Shows the button UI when both full-screen
- * and window magnification mode are capable, and when the magnification scale is changed. And
- * the button UI would automatically be dismissed after displaying for a period of time.
+ * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
+ * conditions:
+ * <ol>
+ * <li> Both full-screen and window magnification mode are capable.</li>
+ * <li> The magnification scale is changed by a user.</li>
+ * <ol>
*/
@Singleton
public class ModeSwitchesController {
- private static final String TAG = "ModeSwitchesController";
-
- private final Context mContext;
- private final DisplayManager mDisplayManager;
-
- private final SparseArray<MagnificationModeSwitch> mDisplaysToSwitches =
- new SparseArray<>();
+ private final SwitchSupplier mSwitchSupplier;
public ModeSwitchesController(Context context) {
- mContext = context;
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mSwitchSupplier = new SwitchSupplier(context,
+ context.getSystemService(DisplayManager.class));
+ }
+
+ @VisibleForTesting
+ ModeSwitchesController(SwitchSupplier switchSupplier) {
+ mSwitchSupplier = switchSupplier;
}
/**
@@ -52,20 +54,17 @@
*
* @param displayId The logical display id
* @param mode The magnification mode
- *
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
*/
@MainThread
void showButton(int displayId, int mode) {
- if (mDisplaysToSwitches.get(displayId) == null) {
- final MagnificationModeSwitch magnificationModeSwitch =
- createMagnificationSwitchController(displayId);
- if (magnificationModeSwitch == null) {
- return;
- }
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
+ return;
}
- mDisplaysToSwitches.get(displayId).showButton(mode);
+ magnificationModeSwitch.showButton(mode);
}
/**
@@ -74,30 +73,34 @@
* @param displayId The logical display id
*/
void removeButton(int displayId) {
- if (mDisplaysToSwitches.get(displayId) == null) {
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
return;
}
- mDisplaysToSwitches.get(displayId).removeButton();
+ magnificationModeSwitch.removeButton();
}
- private MagnificationModeSwitch createMagnificationSwitchController(int displayId) {
- if (mDisplayManager.getDisplay(displayId) == null) {
- Log.w(TAG, "createMagnificationSwitchController displayId is invalid.");
- return null;
+ @VisibleForTesting
+ static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
+
+ private final Context mContext;
+
+ /**
+ * @param context Context
+ * @param displayManager DisplayManager
+ */
+ SwitchSupplier(Context context, DisplayManager displayManager) {
+ super(displayManager);
+ mContext = context;
}
- final MagnificationModeSwitch
- magnificationModeSwitch = new MagnificationModeSwitch(
- getDisplayContext(displayId));
- mDisplaysToSwitches.put(displayId, magnificationModeSwitch);
- return magnificationModeSwitch;
- }
- private Context getDisplayContext(int displayId) {
- final Display display = mDisplayManager.getDisplay(displayId);
- final Context context = (displayId == Display.DEFAULT_DISPLAY)
- ? mContext
- : mContext.createDisplayContext(display);
- return context;
+ @Override
+ protected MagnificationModeSwitch createInstance(Display display) {
+ final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
+ ? mContext
+ : mContext.createDisplayContext(display);
+ return new MagnificationModeSwitch(context);
+ }
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 5fd7b53..4df6660 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -18,6 +18,7 @@
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -25,11 +26,14 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.WorkerThread;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,6 +66,7 @@
private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
+ private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
@GuardedBy("mActiveItems")
@@ -82,8 +87,11 @@
public AppOpsControllerImpl(
Context context,
@Background Looper bgLooper,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ PermissionFlagsCache cache
+ ) {
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mFlagsCache = cache;
mBGHandler = new H(bgLooper);
final int numOps = OPS.length;
for (int i = 0; i < numOps; i++) {
@@ -230,10 +238,66 @@
}
/**
+ * Does the app-op code refer to a user sensitive permission for the specified user id
+ * and package. Only user sensitive permission should be shown to the user by default.
+ *
+ * @param appOpCode The code of the app-op.
+ * @param uid The uid of the user.
+ * @param packageName The name of the package.
+ *
+ * @return {@code true} iff the app-op item is user sensitive
+ */
+ private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
+ String permission = AppOpsManager.opToPermission(appOpCode);
+ if (permission == null) {
+ return false;
+ }
+ int permFlags = mFlagsCache.getPermissionFlags(permission,
+ packageName, uid);
+ return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
+ }
+
+ /**
+ * Does the app-op item refer to an operation that should be shown to the user.
+ * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
+ * permission should be shown to the user by default.
+ *
+ * @param item The item
+ *
+ * @return {@code true} iff the app-op item should be shown to the user
+ */
+ private boolean isUserVisible(AppOpItem item) {
+ return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
+ }
+
+
+ /**
+ * Does the app-op, uid and package name, refer to an operation that should be shown to the
+ * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
+ * ops that refer to user sensitive permission should be shown to the user by default.
+ *
+ * @param item The item
+ *
+ * @return {@code true} iff the app-op for should be shown to the user
+ */
+ private boolean isUserVisible(int appOpCode, int uid, String packageName) {
+ // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
+ // which may be user senstive, so for now always show it to the user.
+ if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
+ return true;
+ }
+
+ return isUserSensitive(appOpCode, uid, packageName);
+ }
+
+ /**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
+ * Call from a worker thread as it may perform long operations.
+ *
* @return List of active AppOps information
*/
+ @WorkerThread
public List<AppOpItem> getActiveAppOps() {
return getActiveAppOpsForUser(UserHandle.USER_ALL);
}
@@ -242,18 +306,23 @@
* Returns a copy of the list containing all the active AppOps that the controller tracks, for
* a given user id.
*
+ * Call from a worker thread as it may perform long operations.
+ *
* @param userId User id to track, can be {@link UserHandle#USER_ALL}
*
* @return List of active AppOps information for that user id
*/
+ @WorkerThread
public List<AppOpItem> getActiveAppOpsForUser(int userId) {
+ Assert.isNotMainThread();
List<AppOpItem> list = new ArrayList<>();
synchronized (mActiveItems) {
final int numActiveItems = mActiveItems.size();
for (int i = 0; i < numActiveItems; i++) {
AppOpItem item = mActiveItems.get(i);
if ((userId == UserHandle.USER_ALL
- || UserHandle.getUserId(item.getUid()) == userId)) {
+ || UserHandle.getUserId(item.getUid()) == userId)
+ && isUserVisible(item)) {
list.add(item);
}
}
@@ -263,7 +332,8 @@
for (int i = 0; i < numNotedItems; i++) {
AppOpItem item = mNotedItems.get(i);
if ((userId == UserHandle.USER_ALL
- || UserHandle.getUserId(item.getUid()) == userId)) {
+ || UserHandle.getUserId(item.getUid()) == userId)
+ && isUserVisible(item)) {
list.add(item);
}
}
@@ -311,7 +381,7 @@
}
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
- if (mCallbacksByCode.contains(code)) {
+ if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
new file mode 100644
index 0000000..45ed78f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Assert
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private data class PermissionFlagKey(
+ val permission: String,
+ val packageName: String,
+ val uid: Int
+)
+
+/**
+ * Cache for PackageManager's PermissionFlags.
+ *
+ * After a specific `{permission, package, uid}` has been requested, updates to it will be tracked,
+ * and changes to the uid will trigger new requests (in the background).
+ */
+@Singleton
+class PermissionFlagsCache @Inject constructor(
+ private val packageManager: PackageManager,
+ @Background private val executor: Executor
+) : PackageManager.OnPermissionsChangedListener {
+
+ private val permissionFlagsCache =
+ mutableMapOf<Int, MutableMap<PermissionFlagKey, Int>>()
+ private var listening = false
+
+ override fun onPermissionsChanged(uid: Int) {
+ executor.execute {
+ // Only track those that we've seen before
+ val keys = permissionFlagsCache.get(uid)
+ if (keys != null) {
+ keys.mapValuesTo(keys) {
+ getFlags(it.key)
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieve permission flags from cache or PackageManager. There parameters will be passed
+ * directly to [PackageManager].
+ *
+ * Calls to this method should be done from a background thread (though it will only be
+ * enforced if the cache is not hit).
+ */
+ @WorkerThread
+ fun getPermissionFlags(permission: String, packageName: String, uid: Int): Int {
+ if (!listening) {
+ listening = true
+ packageManager.addOnPermissionsChangeListener(this)
+ }
+ val key = PermissionFlagKey(permission, packageName, uid)
+ return permissionFlagsCache.getOrPut(uid, { mutableMapOf() }).get(key) ?: run {
+ getFlags(key).also {
+ Assert.isNotMainThread()
+ permissionFlagsCache.get(uid)?.put(key, it)
+ }
+ }
+ }
+
+ private fun getFlags(key: PermissionFlagKey): Int {
+ return packageManager.getPermissionFlags(key.permission, key.packageName,
+ UserHandle.getUserHandleForUid(key.uid))
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 980e4c0..361ea67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,7 +29,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -39,12 +38,10 @@
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.IFingerprintService;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
import android.view.WindowManager;
@@ -247,6 +244,10 @@
IActivityTaskManager getActivityTaskManager() {
return ActivityTaskManager.getService();
}
+
+ FingerprintManager getFingerprintManager(Context context) {
+ return context.getSystemService(FingerprintManager.class);
+ }
}
@Inject
@@ -273,7 +274,7 @@
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mActivityTaskManager = mInjector.getActivityTaskManager();
- final FingerprintManager fpm = mContext.getSystemService(FingerprintManager.class);
+ final FingerprintManager fpm = mInjector.getFingerprintManager(mContext);
if (fpm != null && fpm.isHardwareDetected()) {
final List<FingerprintSensorProperties> fingerprintSensorProperties =
fpm.getSensorProperties();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
deleted file mode 100644
index 9db371e..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import com.android.systemui.R;
-
-/** Dismiss view that contains a scrim gradient, as well as a dismiss icon, text, and circle. */
-public class BubbleDismissView extends FrameLayout {
- /** Duration for animations involving the dismiss target text/icon. */
- private static final int DISMISS_TARGET_ANIMATION_BASE_DURATION = 150;
- private static final float SCALE_FOR_POP = 1.2f;
- private static final float SCALE_FOR_DISMISS = 0.9f;
-
- private LinearLayout mDismissTarget;
- private ImageView mDismissIcon;
- private View mDismissCircle;
-
- private SpringAnimation mDismissTargetAlphaSpring;
- private SpringAnimation mDismissTargetVerticalSpring;
-
- public BubbleDismissView(Context context) {
- super(context);
- setVisibility(GONE);
-
- LayoutInflater.from(context).inflate(R.layout.bubble_dismiss_target, this, true);
- mDismissTarget = findViewById(R.id.bubble_dismiss_icon_container);
- mDismissIcon = findViewById(R.id.bubble_dismiss_close_icon);
- mDismissCircle = findViewById(R.id.bubble_dismiss_circle);
-
- // Set up the basic target area animations. These are very simple animations that don't need
- // fancy interpolators.
- final AccelerateDecelerateInterpolator interpolator =
- new AccelerateDecelerateInterpolator();
- mDismissIcon.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION)
- .setInterpolator(interpolator);
- mDismissCircle.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION / 2)
- .setInterpolator(interpolator);
-
- mDismissTargetAlphaSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.ALPHA)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
- mDismissTargetVerticalSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.TRANSLATION_Y)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_MEDIUM)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
-
- mDismissTargetAlphaSpring.addEndListener((anim, canceled, alpha, velocity) -> {
- // Since DynamicAnimations end when they're 'nearly' done, we can't rely on alpha being
- // exactly zero when this listener is triggered. However, if it's less than 50% we can
- // safely assume it was animating out rather than in.
- if (alpha < 0.5f) {
- // If the alpha spring was animating the view out, set it to GONE when it's done.
- setVisibility(INVISIBLE);
- }
- });
- }
-
- /** Springs in the dismiss target. */
- void springIn() {
- setVisibility(View.VISIBLE);
-
- // Fade in the dismiss target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f);
- mDismissTarget.setAlpha(0f);
- mDismissTargetAlphaSpring.animateToFinalPosition(1f);
-
- // Spring up the dismiss target.
- mDismissTarget.setTranslationY(mDismissTarget.getHeight() / 2f);
- mDismissTargetVerticalSpring.animateToFinalPosition(0);
-
- mDismissCircle.setAlpha(0f);
- mDismissCircle.setScaleX(SCALE_FOR_POP);
- mDismissCircle.setScaleY(SCALE_FOR_POP);
-
- // Fade in circle and reduce size.
- mDismissCircle.animate()
- .alpha(1f)
- .scaleX(1f)
- .scaleY(1f);
- }
-
- /** Springs out the dismiss target. */
- void springOut() {
- // Fade out the target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
-
- // Fade out the target.
- mDismissTargetAlphaSpring.animateToFinalPosition(0f);
-
- // Spring the target down a bit.
- mDismissTargetVerticalSpring.animateToFinalPosition(mDismissTarget.getHeight() / 2f);
-
- // Pop out the circle.
- mDismissCircle.animate()
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
- }
-
- /** Returns the Y value of the center of the dismiss target. */
- float getDismissTargetCenterY() {
- return getTop() + mDismissTarget.getTop() + mDismissTarget.getHeight() / 2f;
- }
-
- /** Returns the dismiss target, which contains the text/icon and any added padding. */
- View getDismissTarget() {
- return mDismissTarget;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
deleted file mode 100644
index 86244ba..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
-
-/**
- * Educational view to highlight the manage button that allows a user to configure the settings
- * for the bubble. Shown only the first time a user expands a bubble.
- */
-public class BubbleManageEducationView extends LinearLayout {
-
- private View mManageView;
- private TextView mTitleTextView;
- private TextView mDescTextView;
-
- public BubbleManageEducationView(Context context) {
- this(context, null);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mManageView = findViewById(R.id.manage_education_view);
- mTitleTextView = findViewById(R.id.user_education_title);
- mDescTextView = findViewById(R.id.user_education_description);
-
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {android.R.attr.colorAccent,
- android.R.attr.textColorPrimaryInverse});
- final int bgColor = ta.getColor(0, Color.BLACK);
- int textColor = ta.getColor(1, Color.WHITE);
- ta.recycle();
-
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
- mTitleTextView.setTextColor(textColor);
- mDescTextView.setTextColor(textColor);
- }
-
- /**
- * Specifies the position for the manage view.
- */
- public void setManageViewPosition(int x, int y) {
- mManageView.setTranslationX(x);
- mManageView.setTranslationY(y);
- }
-
- /**
- * @return the height of the view that shows the educational text and pointer.
- */
- public int getManageViewHeight() {
- return mManageView.getHeight();
- }
-
- @Override
- public void setLayoutDirection(int direction) {
- super.setLayoutDirection(direction);
- if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
- mTitleTextView.setGravity(Gravity.RIGHT);
- mDescTextView.setGravity(Gravity.RIGHT);
- } else {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
- mTitleTextView.setGravity(Gravity.LEFT);
- mDescTextView.setGravity(Gravity.LEFT);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
deleted file mode 100644
index bb9d109..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.View.GONE;
-
-import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.PathParser;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Class for showing aged out bubbles.
- */
-public class BubbleOverflow implements BubbleViewProvider {
- public static final String KEY = "Overflow";
-
- private BadgedImageView mOverflowBtn;
- private BubbleExpandedView mExpandedView;
- private LayoutInflater mInflater;
- private Context mContext;
- private Bitmap mIcon;
- private Path mPath;
- private int mBitmapSize;
- private int mIconBitmapSize;
- private int mDotColor;
-
- public BubbleOverflow(Context context) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- }
-
- void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
- updateDimensions();
- mExpandedView = (BubbleExpandedView) mInflater.inflate(
- R.layout.bubble_expanded_view, parentViewGroup /* root */,
- false /* attachToRoot */);
- mExpandedView.setOverflow(true);
- mExpandedView.setStackView(stackView);
- mExpandedView.applyThemeAttrs();
- updateIcon(mContext, parentViewGroup);
- }
-
- void updateDimensions() {
- mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
- mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.bubble_overflow_icon_bitmap_size);
- if (mExpandedView != null) {
- mExpandedView.updateDimensions();
- }
- }
-
- void updateIcon(Context context, ViewGroup parentViewGroup) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
- parentViewGroup /* root */,
- false /* attachToRoot */);
- mOverflowBtn.setContentDescription(mContext.getResources().getString(
- R.string.bubble_overflow_button_content_description));
- Resources res = mContext.getResources();
-
- // Set color for button icon and dot
- TypedValue typedValue = new TypedValue();
- mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
- int colorAccent = mContext.getColor(typedValue.resourceId);
- mOverflowBtn.getDrawable().setTint(colorAccent);
- mDotColor = colorAccent;
-
- // Set color for button and activity background
- ColorDrawable bg = new ColorDrawable(res.getColor(R.color.bubbles_light));
- final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- if (mode == Configuration.UI_MODE_NIGHT_YES) {
- bg = new ColorDrawable(res.getColor(R.color.bubbles_dark));
- }
-
- // Apply icon inset
- InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
- mBitmapSize - mIconBitmapSize /* inset */);
- AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
-
- BubbleIconFactory iconFactory = new BubbleIconFactory(mContext);
- mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */).icon;
-
- // Get path with dot location
- float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
- null /* outBounds */, null /* path */, null /* outMaskShape */);
- float radius = DEFAULT_PATH_SIZE / 2f;
- mPath = PathParser.createPathFromPathData(
- mContext.getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- mPath.transform(matrix);
-
- mOverflowBtn.setRenderedBubble(this);
- }
-
- void setVisible(int visible) {
- mOverflowBtn.setVisibility(visible);
- }
-
- @Override
- public BubbleExpandedView getExpandedView() {
- return mExpandedView;
- }
-
- @Override
- public int getDotColor() {
- return mDotColor;
- }
-
- @Override
- public Bitmap getBadgedImage() {
- return mIcon;
- }
-
- @Override
- public boolean showDot() {
- return false;
- }
-
- @Override
- public Path getDotPath() {
- return mPath;
- }
-
- @Override
- public void setContentVisibility(boolean visible) {
- mExpandedView.setContentVisibility(visible);
- }
-
- @Override
- public View getIconView() {
- return mOverflowBtn;
- }
-
- @Override
- public String getKey() {
- return BubbleOverflow.KEY;
- }
-
- @Override
- public int getDisplayId() {
- return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
new file mode 100644
index 0000000..155b71b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Path
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.InsetDrawable
+import android.util.PathParser
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+class BubbleOverflow(
+ private val context: Context,
+ private val stack: BubbleStackView
+) : BubbleViewProvider {
+
+ private var bitmap: Bitmap? = null
+ private var dotPath: Path? = null
+ private var bitmapSize = 0
+ private var iconBitmapSize = 0
+ private var dotColor = 0
+
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+ private val expandedView: BubbleExpandedView = inflater
+ .inflate(R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */)
+ as BubbleExpandedView
+ private val overflowBtn: BadgedImageView = inflater
+ .inflate(R.layout.bubble_overflow_button, null /* root */, false /* attachToRoot */)
+ as BadgedImageView
+ init {
+ updateResources()
+ with(expandedView) {
+ setOverflow(true)
+ setStackView(stack)
+ applyThemeAttrs()
+ }
+ with(overflowBtn) {
+ setContentDescription(context.resources.getString(
+ R.string.bubble_overflow_button_content_description))
+ updateBtnTheme()
+ }
+ }
+
+ fun update() {
+ updateResources()
+ expandedView.applyThemeAttrs()
+ // Apply inset and new style to fresh icon drawable.
+ overflowBtn.setImageResource(R.drawable.ic_bubble_overflow_button)
+ updateBtnTheme()
+ }
+
+ fun updateResources() {
+ bitmapSize = context.resources.getDimensionPixelSize(R.dimen.bubble_bitmap_size)
+ iconBitmapSize = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_overflow_icon_bitmap_size)
+ val bubbleSize = context.resources.getDimensionPixelSize(R.dimen.individual_bubble_size)
+ overflowBtn.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+ expandedView.updateDimensions()
+ }
+
+ fun updateBtnTheme() {
+ val res = context.resources
+
+ // Set overflow button accent color, dot color
+ val typedValue = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
+
+ val colorAccent = res.getColor(typedValue.resourceId)
+ overflowBtn.getDrawable()?.setTint(colorAccent)
+ dotColor = colorAccent
+
+ // Set button and activity background color
+ val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ == Configuration.UI_MODE_NIGHT_YES)
+ val bg = ColorDrawable(res.getColor(
+ if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+
+ // Set button icon
+ val iconFactory = BubbleIconFactory(context)
+ val fg = InsetDrawable(overflowBtn.getDrawable(),
+ bitmapSize - iconBitmapSize /* inset */)
+ bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+ null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+ // Set dot path
+ dotPath = PathParser.createPathFromPathData(
+ res.getString(com.android.internal.R.string.config_icon_mask))
+ val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
+ null /* outBounds */, null /* path */, null /* outMaskShape */)
+ val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
+ val matrix = Matrix()
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */)
+ dotPath?.transform(matrix)
+ overflowBtn.setRenderedBubble(this)
+ }
+
+ fun setVisible(visible: Int) {
+ overflowBtn.visibility = visible
+ }
+
+ override fun getExpandedView(): BubbleExpandedView? {
+ return expandedView
+ }
+
+ override fun getDotColor(): Int {
+ return dotColor
+ }
+
+ override fun getBadgedImage(): Bitmap? {
+ return bitmap
+ }
+
+ override fun showDot(): Boolean {
+ return false
+ }
+
+ override fun getDotPath(): Path? {
+ return dotPath
+ }
+
+ override fun setContentVisibility(visible: Boolean) {
+ expandedView.setContentVisibility(visible)
+ }
+
+ override fun getIconView(): View? {
+ return overflowBtn
+ }
+
+ override fun getKey(): String {
+ return KEY
+ }
+
+ override fun getDisplayId(): Int {
+ return expandedView.virtualDisplayId
+ }
+
+ companion object {
+ @JvmField val KEY = "Overflow"
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index f02945e..ea12c95 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -48,7 +48,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
@@ -95,7 +94,6 @@
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.RelativeTouchListener;
import com.android.systemui.util.animation.PhysicsAnimator;
@@ -118,7 +116,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
/** Animation durations for bubble stack user education views. **/
- private static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
+ static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -139,9 +137,6 @@
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
- /** Duration of the dismiss scrim fading in/out. */
- private static final int DISMISS_TRANSITION_DURATION_MS = 200;
-
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
@@ -300,7 +295,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Stack view state:");
pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
- pw.print(" showingDismiss: "); pw.println(mShowingDismiss);
+ pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
pw.print(" expandedContainerVis: "); pw.println(mExpandedViewContainer.getVisibility());
pw.print(" expandedContainerAlpha: "); pw.println(mExpandedViewContainer.getAlpha());
@@ -347,7 +342,6 @@
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
private boolean mIsBubbleSwitchAnimating = false;
- private boolean mShowingDismiss = false;
/** The view to desaturate/darken when magneted to the dismiss target. */
@Nullable private View mDesaturateAndDarkenTargetView;
@@ -465,7 +459,7 @@
if (wasFlungOut) {
mExpandedAnimationController.snapBubbleBack(
mExpandedAnimationController.getDraggedOutBubble(), velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mExpandedAnimationController.onUnstuckFromTarget();
}
@@ -479,9 +473,9 @@
mExpandedAnimationController.dismissDraggedOutBubble(
mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
BubbleStackView.this::dismissMagnetizedObject /* after */);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -502,7 +496,7 @@
if (wasFlungOut) {
mStackAnimationController.flingStackThenSpringToEdge(
mStackAnimationController.getStackPosition().x, velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mStackAnimationController.onUnstuckFromTarget();
}
@@ -511,14 +505,14 @@
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mStackAnimationController.animateStackDismissal(
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
() -> {
resetDesaturationAndDarken();
dismissMagnetizedObject();
}
);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -639,7 +633,7 @@
}
// Show the dismiss target, if we haven't already.
- springInDismissTargetMaybe();
+ mDismissView.show();
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
@@ -681,7 +675,7 @@
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
- hideDismissTarget();
+ mDismissView.hide();
}
mIsDraggingStack = false;
@@ -743,12 +737,7 @@
}
};
- private View mDismissTargetCircle;
- private ViewGroup mDismissTargetContainer;
- private PhysicsAnimator<View> mDismissTargetAnimator;
- private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
+ private DismissView mDismissView;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
@Nullable
@@ -759,7 +748,7 @@
private View mUserEducationView;
private boolean mShouldShowManageEducation;
- private BubbleManageEducationView mManageEducationView;
+ private ManageEducationView mManageEducationView;
private boolean mAnimatingManageEducationAway;
private ViewGroup mManageMenu;
@@ -866,34 +855,8 @@
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle = new DismissCircleView(context);
- final FrameLayout.LayoutParams newParams =
- new FrameLayout.LayoutParams(targetSize, targetSize);
- newParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- mDismissTargetCircle.setLayoutParams(newParams);
- mDismissTargetAnimator = PhysicsAnimator.getInstance(mDismissTargetCircle);
-
- mDismissTargetContainer = new FrameLayout(context);
- mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams(
- MATCH_PARENT,
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
- Gravity.BOTTOM));
-
- final int bottomMargin =
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin);
- mDismissTargetContainer.setPadding(0, 0, 0, bottomMargin);
- mDismissTargetContainer.setClipToPadding(false);
- mDismissTargetContainer.setClipChildren(false);
- mDismissTargetContainer.addView(mDismissTargetCircle);
- mDismissTargetContainer.setVisibility(View.INVISIBLE);
- mDismissTargetContainer.setBackgroundResource(
- R.drawable.floating_dismiss_gradient_transition);
- addView(mDismissTargetContainer);
-
- // Start translated down so the target springs up.
- mDismissTargetCircle.setTranslationY(
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
+ mDismissView = new DismissView(context);
+ addView(mDismissView);
final ContentResolver contentResolver = getContext().getContentResolver();
final int dismissRadius = Settings.Secure.getInt(
@@ -901,13 +864,23 @@
// Save the MagneticTarget instance for the newly set up view - we'll add this to the
// MagnetizedObjects.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(mDismissTargetCircle, dismissRadius);
+ mMagneticTarget = new MagnetizedObject.MagneticTarget(
+ mDismissView.getCircle(), dismissRadius);
setClipChildren(false);
setFocusable(true);
mBubbleContainer.bringToFront();
- setUpOverflow();
+ mBubbleOverflow = new BubbleOverflow(getContext(), this);
+ mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() /* index */,
+ new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ updateOverflow();
+ mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+ setSelectedBubble(mBubbleOverflow);
+ showManageMenu(false);
+ });
mOnImeVisibilityChanged = onImeVisibilityChanged;
mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
@@ -933,7 +906,7 @@
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
- mBubbleOverflow.updateDimensions();
+ mBubbleOverflow.updateResources();
// Need to update the padding around the view
WindowInsets insets = getRootWindowInsets();
@@ -1162,12 +1135,9 @@
Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
}
if (mShouldShowManageEducation) {
- mManageEducationView = (BubbleManageEducationView)
- mInflater.inflate(R.layout.bubbles_manage_button_education, this,
+ mManageEducationView = (ManageEducationView)
+ mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
false /* attachToRoot */);
- mManageEducationView.setVisibility(GONE);
- mManageEducationView.setElevation(mBubbleElevation);
- mManageEducationView.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
addView(mManageEducationView);
}
}
@@ -1187,32 +1157,21 @@
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
}
- private void setUpOverflow() {
- int overflowBtnIndex = 0;
- if (mBubbleOverflow == null) {
- mBubbleOverflow = new BubbleOverflow(getContext());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- } else {
- mBubbleContainer.removeView(mBubbleOverflow.getIconView());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- overflowBtnIndex = mBubbleContainer.getChildCount();
- }
- mBubbleContainer.addView(mBubbleOverflow.getIconView(), overflowBtnIndex,
- new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mBubbleOverflow.getIconView().setOnClickListener(v -> {
- setSelectedBubble(mBubbleOverflow);
- showManageMenu(false);
- });
+ private void updateOverflow() {
+ mBubbleOverflow.update();
+ mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() - 1 /* index */);
updateOverflowVisibility();
}
+
/**
* Handle theme changes.
*/
public void onThemeChanged() {
setUpFlyout();
- setUpOverflow();
setUpUserEducation();
setUpManageMenu();
+ updateOverflow();
updateExpandedViewTheme();
}
@@ -1261,7 +1220,7 @@
/** Respond to the display size change by recalculating view size and location. */
public void onDisplaySizeChanged() {
- setUpOverflow();
+ updateOverflow();
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getRealSize(mDisplaySize);
@@ -1279,12 +1238,7 @@
}
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
-
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle.getLayoutParams().width = targetSize;
- mDismissTargetCircle.getLayoutParams().height = targetSize;
- mDismissTargetCircle.requestLayout();
-
+ mDismissView.updateResources();
mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
}
@@ -1796,28 +1750,8 @@
&& mManageEducationView.getVisibility() != VISIBLE
&& mIsExpanded
&& mExpandedBubble.getExpandedView() != null) {
- mManageEducationView.setAlpha(0);
- mManageEducationView.setVisibility(VISIBLE);
- mManageEducationView.post(() -> {
- mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
- final int viewHeight = mManageEducationView.getManageViewHeight();
- final int inset = getResources().getDimensionPixelSize(
- R.dimen.bubbles_manage_education_top_inset);
- mManageEducationView.bringToFront();
- mManageEducationView.setManageViewPosition(0, mTempRect.top - viewHeight + inset);
- mManageEducationView.animate()
- .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
- .setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
- mManageEducationView.findViewById(R.id.manage).setOnClickListener(view -> {
- mExpandedBubble.getExpandedView().findViewById(R.id.settings_button)
- .performClick();
- maybeShowManageEducation(false);
- });
- mManageEducationView.findViewById(R.id.got_it).setOnClickListener(view ->
- maybeShowManageEducation(false));
- mManageEducationView.setOnClickListener(view ->
- maybeShowManageEducation(false));
- });
+ mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
+ () -> maybeShowManageEducation(false) /* run on click */);
Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
} else if (!show
&& mManageEducationView.getVisibility() == VISIBLE
@@ -2362,48 +2296,6 @@
}
}
- /** Animates in the dismiss target. */
- private void springInDismissTargetMaybe() {
- if (mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = true;
-
- mDismissTargetContainer.bringToFront();
- mDismissTargetContainer.setZ(Short.MAX_VALUE - 1);
- mDismissTargetContainer.setVisibility(VISIBLE);
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).startTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator.cancel();
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, 0f, mDismissTargetSpring)
- .start();
- }
-
- /**
- * Animates the dismiss target out, as well as the circle that encircles the bubbles, if they
- * were dragged into the target and encircled.
- */
- private void hideDismissTarget() {
- if (!mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = false;
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).reverseTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, mDismissTargetContainer.getHeight(),
- mDismissTargetSpring)
- .withEndActions(() -> mDismissTargetContainer.setVisibility(View.INVISIBLE))
- .start();
- }
-
/** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */
private void animateFlyoutCollapsed(boolean collapsed, float velX) {
final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
new file mode 100644
index 0000000..71faf4a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
@@ -0,0 +1,85 @@
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.drawable.TransitionDrawable
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
+import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
+import com.android.systemui.R
+import com.android.systemui.util.DismissCircleView
+import com.android.systemui.util.animation.PhysicsAnimator
+
+/*
+ * View that handles interactions between DismissCircleView and BubbleStackView.
+ */
+class DismissView(context: Context) : FrameLayout(context) {
+
+ var circle = DismissCircleView(context).apply {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ val newParams = LayoutParams(targetSize, targetSize)
+ newParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ setLayoutParams(newParams)
+ setTranslationY(
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height).toFloat())
+ }
+
+ var isShowing = false
+ private val animator = PhysicsAnimator.getInstance(circle)
+ private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY);
+ private val DISMISS_SCRIM_FADE_MS = 200
+ init {
+ setLayoutParams(LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
+ Gravity.BOTTOM))
+ setPadding(0, 0, 0, resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+ setClipToPadding(false)
+ setClipChildren(false)
+ setVisibility(View.INVISIBLE)
+ setBackgroundResource(
+ R.drawable.floating_dismiss_gradient_transition)
+ addView(circle)
+ }
+
+ /**
+ * Animates this view in.
+ */
+ fun show() {
+ if (isShowing) return
+ isShowing = true
+ bringToFront()
+ setZ(Short.MAX_VALUE - 1f)
+ setVisibility(View.VISIBLE)
+ (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
+ animator.cancel()
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
+ .start()
+ }
+
+ /**
+ * Animates this view out, as well as the circle that encircles the bubbles, if they
+ * were dragged into the target and encircled.
+ */
+ fun hide() {
+ if (!isShowing) return
+ isShowing = false
+ (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS)
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
+ spring)
+ .withEndActions({ setVisibility(View.INVISIBLE) })
+ .start()
+ }
+
+ fun updateResources() {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ circle.layoutParams.width = targetSize
+ circle.layoutParams.height = targetSize
+ circle.requestLayout()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
new file mode 100644
index 0000000..c58ab31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+
+/**
+ * Educational view to highlight the manage button that allows a user to configure the settings
+ * for the bubble. Shown only the first time a user expands a bubble.
+ */
+class ManageEducationView @JvmOverloads constructor(
+ context: Context?,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
+ private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+ private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
+ private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
+ private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
+ private var isInflated = false
+
+ init {
+ this.visibility = View.GONE
+ this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+ this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ }
+
+ override fun setLayoutDirection(direction: Int) {
+ super.setLayoutDirection(direction)
+ // setLayoutDirection runs before onFinishInflate
+ // so skip if views haven't inflated; otherwise we'll get NPEs
+ if (!isInflated) return
+ setDirection()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ isInflated = true
+ setDirection()
+ setTextColor()
+ }
+
+ private fun setTextColor() {
+ val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
+ android.R.attr.textColorPrimaryInverse))
+ val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
+ var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+ typedArray.recycle()
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
+ titleTextView.setTextColor(textColor)
+ descTextView.setTextColor(textColor)
+ }
+
+ fun setDirection() {
+ manageView.setBackgroundResource(
+ if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
+ R.drawable.bubble_stack_user_education_bg_rtl
+ else R.drawable.bubble_stack_user_education_bg)
+ titleTextView.gravity = Gravity.START
+ descTextView.gravity = Gravity.START
+ }
+
+ fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) {
+ alpha = 0f
+ visibility = View.VISIBLE
+ post {
+ expandedView.getManageButtonBoundsOnScreen(rect)
+ with(hideMenu) {
+ manageButton
+ .setOnClickListener {
+ expandedView.findViewById<View>(R.id.settings_button).performClick()
+ this.run()
+ }
+ gotItButton.setOnClickListener { this.run() }
+ setOnClickListener { this.run() }
+ }
+ with(manageView) {
+ translationX = 0f
+ val inset = resources.getDimensionPixelSize(
+ R.dimen.bubbles_manage_education_top_inset)
+ translationY = (rect.top - manageView.height + inset).toFloat()
+ }
+ bringToFront()
+ animate()
+ .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong())
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 56d0fa2..6e8d63b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,7 +18,9 @@
import android.content.BroadcastReceiver;
-import com.android.systemui.screenshot.GlobalScreenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.DeleteScreenshotReceiver;
+import com.android.systemui.screenshot.SmartActionsReceiver;
import dagger.Binds;
import dagger.Module;
@@ -30,10 +32,31 @@
*/
@Module
public abstract class DefaultBroadcastReceiverBinder {
- /** */
+ /**
+ *
+ */
@Binds
@IntoMap
@ClassKey(ActionProxyReceiver.class)
public abstract BroadcastReceiver bindActionProxyReceiver(
ActionProxyReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(DeleteScreenshotReceiver.class)
+ public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
+ DeleteScreenshotReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(SmartActionsReceiver.class)
+ public abstract BroadcastReceiver bindSmartActionsReceiver(
+ SmartActionsReceiver broadcastReceiver);
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4bd046e..fce545b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -45,6 +45,7 @@
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.SensorModule;
+import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
@@ -65,7 +66,8 @@
LogModule.class,
PeopleHubModule.class,
SensorModule.class,
- SettingsModule.class
+ SettingsModule.class,
+ SettingsUtilModule.class
},
subcomponents = {StatusBarComponent.class,
NotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e5cc1384..900c11f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -19,12 +19,12 @@
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import android.content.ContentProvider;
+import android.content.Context;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactory;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -37,6 +37,7 @@
import javax.inject.Named;
import javax.inject.Singleton;
+import dagger.BindsInstance;
import dagger.Component;
/**
@@ -50,13 +51,23 @@
OneHandedModule.class,
PipModule.class,
SystemServicesModule.class,
- SystemUIFactory.ContextHolder.class,
SystemUIBinder.class,
SystemUIModule.class,
SystemUIDefaultModule.class})
public interface SystemUIRootComponent {
/**
+ * Builder for a SystemUIRootComponent.
+ */
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ Builder context(Context context);
+
+ SystemUIRootComponent build();
+ }
+
+ /**
* Provides a BootCompleteCache.
*/
@Singleton
@@ -78,7 +89,7 @@
* Main dependency providing module.
*/
@Singleton
- Dependency.DependencyInjector createDependency();
+ Dependency createDependency();
/** */
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index b9d23ad..1ef806c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -364,9 +364,6 @@
Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
return mState;
}
- if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
- return State.DOZE;
- }
if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 37bdda8..524d9c8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -21,7 +21,6 @@
import android.annotation.AnyThread;
import android.app.ActivityManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
@@ -49,6 +48,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -64,10 +64,10 @@
private final Context mContext;
private final AsyncSensorManager mSensorManager;
- private final ContentResolver mResolver;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
+ private final SecureSettings mSecureSettings;
private final Callback mCallback;
@VisibleForTesting
protected TriggerSensor[] mSensors;
@@ -98,13 +98,13 @@
DozeSensors(Context context, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
- ProximitySensor proximitySensor) {
+ ProximitySensor proximitySensor, SecureSettings secureSettings) {
mContext = context;
mSensorManager = sensorManager;
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
- mResolver = mContext.getContentResolver();
+ mSecureSettings = secureSettings;
mCallback = callback;
mProximitySensor = proximitySensor;
@@ -241,7 +241,7 @@
}
if (!anyListening) {
- mResolver.unregisterContentObserver(mSettingsObserver);
+ mSecureSettings.unregisterContentObserver(mSettingsObserver);
} else if (!mSettingRegistered) {
for (TriggerSensor s : mSensors) {
s.registerSettingsObserver(mSettingsObserver);
@@ -400,7 +400,7 @@
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
- return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0,
+ return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
UserHandle.USER_CURRENT) != 0;
}
@@ -444,9 +444,8 @@
public void registerSettingsObserver(ContentObserver settingsObserver) {
if (mConfigured && !TextUtils.isEmpty(mSetting)) {
- mResolver.registerContentObserver(
- Settings.Secure.getUriFor(mSetting), false /* descendants */,
- mSettingsObserver, UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUser(
+ mSetting, mSettingsObserver, UserHandle.USER_ALL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index abbbd17..e38dce0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -46,6 +46,7 @@
import com.android.systemui.util.Assert;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -163,7 +164,8 @@
DozeParameters dozeParameters, AsyncSensorManager sensorManager,
WakeLock wakeLock, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
- DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
+ DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
+ SecureSettings secureSettings) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -172,7 +174,8 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = true;
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
+ config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
+ secureSettings);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 662831e..24ca970 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -27,6 +27,7 @@
import javax.inject.Singleton
private const val TAG = "MediaDataFilter"
+private const val DEBUG = true
/**
* Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
@@ -98,7 +99,7 @@
// are up to date
mediaEntries.clear()
keyCopy.forEach {
- Log.d(TAG, "Removing $it after user change")
+ if (DEBUG) Log.d(TAG, "Removing $it after user change")
listenersCopy.forEach { listener ->
listener.onMediaDataRemoved(it)
}
@@ -106,7 +107,7 @@
dataSource.getData().forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- Log.d(TAG, "Re-adding $key after user change")
+ if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
mediaEntries.put(key, data)
listenersCopy.forEach { listener ->
listener.onMediaDataLoaded(key, null, data)
@@ -119,6 +120,7 @@
* Invoked when the user has dismissed the media carousel
*/
fun onSwipeToDismiss() {
+ if (DEBUG) Log.d(TAG, "Media carousel swiped away")
val mediaKeys = mediaEntries.keys.toSet()
mediaKeys.forEach {
mediaDataManager.setTimedOut(it, timedOut = true)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index b327773..d82150f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -63,6 +63,7 @@
)
private const val TAG = "MediaDataManager"
+private const val DEBUG = true
private const val DEFAULT_LUMINOSITY = 0.25f
private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
@@ -253,7 +254,7 @@
fun removeListener(listener: Listener) = listeners.remove(listener)
/**
- * Called whenever the player has been paused or stopped for a while.
+ * Called whenever the player has been paused or stopped for a while, or swiped from QQS.
* This will make the player not active anymore, hiding it from QQS and Keyguard.
* @see MediaData.active
*/
@@ -263,6 +264,7 @@
return
}
it.active = !timedOut
+ if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut")
onMediaDataLoaded(token, token, it)
}
}
@@ -283,7 +285,9 @@
return
}
- Log.d(TAG, "adding track for $userId from browser: $desc")
+ if (DEBUG) {
+ Log.d(TAG, "adding track for $userId from browser: $desc")
+ }
// Album art
var artworkBitmap = desc.iconBitmap
@@ -383,7 +387,7 @@
if (actions != null) {
for ((index, action) in actions.withIndex()) {
if (action.getIcon() == null) {
- Log.i(TAG, "No icon for action $index ${action.title}")
+ if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
actionsToShowCollapsed.remove(index)
continue
}
@@ -427,7 +431,7 @@
if (!TextUtils.isEmpty(uriString)) {
val albumArt = loadBitmapFromUri(Uri.parse(uriString))
if (albumArt != null) {
- Log.d(TAG, "loaded art from $uri")
+ if (DEBUG) Log.d(TAG, "loaded art from $uri")
return albumArt
}
}
@@ -514,7 +518,7 @@
Assert.isMainThread()
val removed = mediaEntries.remove(key)
if (useMediaResumption && removed?.resumeAction != null) {
- Log.d(TAG, "Not removing $key because resumable")
+ if (DEBUG) Log.d(TAG, "Not removing $key because resumable")
// Move to resume key (aka package name) if that key doesn't already exist.
val resumeAction = getResumeMediaAction(removed.resumeAction!!)
val updated = removed.copy(token = null, actions = listOf(resumeAction),
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
index 0f4e6be..9239435 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
@@ -29,7 +29,9 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -56,6 +58,7 @@
private static final String TAG = "OneHandedUI";
private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
"com.android.internal.systemui.onehanded.gestural";
+ private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
private final OneHandedManagerImpl mOneHandedManager;
private final CommandQueue mCommandQueue;
@@ -136,10 +139,18 @@
ScreenLifecycle screenLifecycle) {
super(context);
+ if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
+ mCommandQueue = null;
+ mOneHandedManager = null;
+ mOverlayManager = null;
+ mSettingUtil = null;
+ mTimeoutHandler = null;
+ mScreenLifecycle = null;
+ return;
+ }
+
mCommandQueue = commandQueue;
- /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
- boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
- if (!supportOneHanded) return; */
mOneHandedManager = oneHandedManager;
mSettingUtil = settingsUtil;
mTimeoutHandler = OneHandedTimeoutHandler.get();
@@ -150,9 +161,9 @@
@Override
public void start() {
- /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
- boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
- if (!supportOneHanded) return; */
+ if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ return;
+ }
mCommandQueue.addCallback(this);
setupKeyguardUpdateMonitor();
setupScreenObserver();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index eb73e13..582cd04 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -78,7 +78,7 @@
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
private final Rect mTmpNormalBounds = new Rect();
- private final Rect mReentryBounds = new Rect();
+ protected final Rect mReentryBounds = new Rect();
private PipBoundsHandler mPipBoundsHandler;
private InputConsumerController mInputConsumerController;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
new file mode 100644
index 0000000..870e714
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.systemui.R
+
+class OngoingPrivacyChip @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttrs: Int = 0,
+ defStyleRes: Int = 0
+) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
+
+ private val iconMarginExpanded = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_chip_icon_margin_expanded)
+ private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_chip_icon_margin_collapsed)
+ private val iconSize =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+ private val iconColor = context.resources.getColor(
+ R.color.status_bar_clock_color, context.theme)
+ private val sidePadding =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+ private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
+ private lateinit var iconsContainer: LinearLayout
+ private lateinit var back: FrameLayout
+ var expanded = false
+ set(value) {
+ if (value != field) {
+ field = value
+ updateView(PrivacyChipBuilder(context, privacyList))
+ }
+ }
+
+ var privacyList = emptyList<PrivacyItem>()
+ set(value) {
+ field = value
+ updateView(PrivacyChipBuilder(context, field))
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ back = requireViewById(R.id.background)
+ iconsContainer = requireViewById(R.id.icons_container)
+ }
+
+ // Should only be called if the builder icons or app changed
+ private fun updateView(builder: PrivacyChipBuilder) {
+ back.background = if (expanded) backgroundDrawable else null
+ val padding = if (expanded) sidePadding else 0
+ back.setPaddingRelative(padding, 0, padding, 0)
+ fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
+ iconsContainer.removeAllViews()
+ chipBuilder.generateIcons().forEachIndexed { i, it ->
+ it.mutate()
+ it.setTint(iconColor)
+ val image = ImageView(context).apply {
+ setImageDrawable(it)
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
+ iconsContainer.addView(image, iconSize, iconSize)
+ if (i != 0) {
+ val lp = image.layoutParams as MarginLayoutParams
+ lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
+ image.layoutParams = lp
+ }
+ }
+ }
+
+ if (!privacyList.isEmpty()) {
+ generateContentDescription(builder)
+ setIcons(builder, iconsContainer)
+ val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
+ lp.gravity = Gravity.CENTER_VERTICAL or
+ (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
+ iconsContainer.layoutParams = lp
+ } else {
+ iconsContainer.removeAllViews()
+ }
+ requestLayout()
+ }
+
+ private fun generateContentDescription(builder: PrivacyChipBuilder) {
+ val typesText = builder.joinTypes()
+ contentDescription = context.getString(
+ R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
new file mode 100644
index 0000000..1d2e747
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
+
+ val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
+ val types: List<PrivacyType>
+ private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
+ private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
+
+ init {
+ appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
+ .toList()
+ .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
+ { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+ types = itemsList.map { it.privacyType }.distinct().sorted()
+ }
+
+ fun generateIcons() = types.map { it.getIcon(context) }
+
+ private fun <T> List<T>.joinWithAnd(): StringBuilder {
+ return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
+ append(lastSeparator)
+ append(this@joinWithAnd.last())
+ }
+ }
+
+ fun joinTypes(): String {
+ return when (types.size) {
+ 0 -> ""
+ 1 -> types[0].getName(context)
+ else -> types.map { it.getName(context) }.joinWithAnd().toString()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
new file mode 100644
index 0000000..1f24fde
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class PrivacyChipEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Privacy chip is viewed by the user. Logged at most once per time QS is visible")
+ ONGOING_INDICATORS_CHIP_VIEW(601),
+
+ @UiEvent(doc = "Privacy chip is clicked")
+ ONGOING_INDICATORS_CHIP_CLICK(602);
+
+ override fun getId() = _id
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
new file mode 100644
index 0000000..3da1363
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+typealias Privacy = PrivacyType
+
+enum class PrivacyType(val nameId: Int, val iconId: Int) {
+ // This is uses the icons used by the corresponding permission groups in the AndroidManifest
+ TYPE_CAMERA(R.string.privacy_type_camera,
+ com.android.internal.R.drawable.perm_group_camera),
+ TYPE_MICROPHONE(R.string.privacy_type_microphone,
+ com.android.internal.R.drawable.perm_group_microphone),
+ TYPE_LOCATION(R.string.privacy_type_location,
+ com.android.internal.R.drawable.perm_group_location);
+
+ fun getName(context: Context) = context.resources.getString(nameId)
+
+ fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
+}
+
+data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication)
+
+data class PrivacyApplication(val packageName: String, val uid: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
new file mode 100644
index 0000000..d5a14f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.Dumpable
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class PrivacyItemController @Inject constructor(
+ context: Context,
+ private val appOpsController: AppOpsController,
+ @Main uiExecutor: DelayableExecutor,
+ @Background private val bgExecutor: Executor,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val deviceConfigProxy: DeviceConfigProxy,
+ private val userManager: UserManager,
+ dumpManager: DumpManager
+) : Dumpable {
+
+ @VisibleForTesting
+ internal companion object {
+ val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_RECORD_AUDIO,
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION)
+ val intentFilter = IntentFilter().apply {
+ addAction(Intent.ACTION_USER_SWITCHED)
+ addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+ }
+ const val TAG = "PrivacyItemController"
+ }
+
+ @VisibleForTesting
+ internal var privacyList = emptyList<PrivacyItem>()
+ @Synchronized get() = field.toList() // Returns a shallow copy of the list
+ @Synchronized set
+
+ private fun isPermissionsHubEnabled(): Boolean {
+ return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ }
+
+ private var currentUserIds = emptyList<Int>()
+ private var listening = false
+ private val callbacks = mutableListOf<WeakReference<Callback>>()
+ private val internalUiExecutor = MyExecutor(uiExecutor)
+
+ private val notifyChanges = Runnable {
+ val list = privacyList
+ callbacks.forEach { it.get()?.onPrivacyItemsChanged(list) }
+ }
+
+ private val updateListAndNotifyChanges = Runnable {
+ updatePrivacyList()
+ uiExecutor.execute(notifyChanges)
+ }
+
+ var indicatorsAvailable = isPermissionsHubEnabled()
+ private set
+ @VisibleForTesting
+ internal val devicePropertiesChangedListener =
+ object : DeviceConfig.OnPropertiesChangedListener {
+ override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+ if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
+ properties.getKeyset().contains(
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
+ val flag = properties.getBoolean(
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ if (indicatorsAvailable != flag) {
+ // This is happening already in the UI executor, so we can iterate in the
+ indicatorsAvailable = flag
+ callbacks.forEach { it.get()?.onFlagChanged(flag) }
+ }
+
+ internalUiExecutor.updateListeningState()
+ }
+ }
+ }
+
+ private val cb = object : AppOpsController.Callback {
+ override fun onActiveStateChanged(
+ code: Int,
+ uid: Int,
+ packageName: String,
+ active: Boolean
+ ) {
+ val userId = UserHandle.getUserId(uid)
+ if (userId in currentUserIds) {
+ update(false)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal var userSwitcherReceiver = Receiver()
+ set(value) {
+ unregisterReceiver()
+ field = value
+ if (listening) registerReceiver()
+ }
+
+ init {
+ deviceConfigProxy.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ uiExecutor,
+ devicePropertiesChangedListener)
+ dumpManager.registerDumpable(TAG, this)
+ }
+
+ private fun unregisterReceiver() {
+ broadcastDispatcher.unregisterReceiver(userSwitcherReceiver)
+ }
+
+ private fun registerReceiver() {
+ broadcastDispatcher.registerReceiver(userSwitcherReceiver, intentFilter,
+ null /* handler */, UserHandle.ALL)
+ }
+
+ private fun update(updateUsers: Boolean) {
+ bgExecutor.execute {
+ if (updateUsers) {
+ val currentUser = ActivityManager.getCurrentUser()
+ currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ }
+ updateListAndNotifyChanges.run()
+ }
+ }
+
+ /**
+ * Updates listening status based on whether there are callbacks and the indicators are enabled
+ *
+ * This is only called from private (add/remove)Callback and from the config listener, all in
+ * main thread.
+ */
+ private fun setListeningState() {
+ val listen = !callbacks.isEmpty() and indicatorsAvailable
+ if (listening == listen) return
+ listening = listen
+ if (listening) {
+ appOpsController.addCallback(OPS, cb)
+ registerReceiver()
+ update(true)
+ } else {
+ appOpsController.removeCallback(OPS, cb)
+ unregisterReceiver()
+ // Make sure that we remove all indicators and notify listeners if we are not
+ // listening anymore due to indicators being disabled
+ update(false)
+ }
+ }
+
+ private fun addCallback(callback: WeakReference<Callback>) {
+ callbacks.add(callback)
+ if (callbacks.isNotEmpty() && !listening) {
+ internalUiExecutor.updateListeningState()
+ }
+ // Notify this callback if we didn't set to listening
+ else if (listening) {
+ internalUiExecutor.execute(NotifyChangesToCallback(callback.get(), privacyList))
+ }
+ }
+
+ private fun removeCallback(callback: WeakReference<Callback>) {
+ // Removes also if the callback is null
+ callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
+ if (callbacks.isEmpty()) {
+ internalUiExecutor.updateListeningState()
+ }
+ }
+
+ fun addCallback(callback: Callback) {
+ addCallback(WeakReference(callback))
+ }
+
+ fun removeCallback(callback: Callback) {
+ removeCallback(WeakReference(callback))
+ }
+
+ private fun updatePrivacyList() {
+ if (!listening) {
+ privacyList = emptyList()
+ return
+ }
+ val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
+ .mapNotNull { toPrivacyItem(it) }.distinct()
+ privacyList = list
+ }
+
+ private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
+ val type: PrivacyType = when (appOpItem.code) {
+ AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
+ AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
+ else -> return null
+ }
+ val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
+ return PrivacyItem(type, app)
+ }
+
+ interface Callback {
+ fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
+ @JvmDefault
+ fun onFlagChanged(flag: Boolean) {}
+ }
+
+ internal inner class Receiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intentFilter.hasAction(intent.action)) {
+ update(true)
+ }
+ }
+ }
+
+ private class NotifyChangesToCallback(
+ private val callback: Callback?,
+ private val list: List<PrivacyItem>
+ ) : Runnable {
+ override fun run() {
+ callback?.onPrivacyItemsChanged(list)
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("PrivacyItemController state:")
+ pw.println(" Listening: $listening")
+ pw.println(" Current user ids: $currentUserIds")
+ pw.println(" Privacy Items:")
+ privacyList.forEach {
+ pw.print(" ")
+ pw.println(it.toString())
+ }
+ pw.println(" Callbacks:")
+ callbacks.forEach {
+ it.get()?.let {
+ pw.print(" ")
+ pw.println(it.toString())
+ }
+ }
+ }
+
+ private inner class MyExecutor(
+ private val delegate: DelayableExecutor
+ ) : Executor {
+
+ private var listeningCanceller: Runnable? = null
+
+ override fun execute(command: Runnable) {
+ delegate.execute(command)
+ }
+
+ fun updateListeningState() {
+ listeningCanceller?.run()
+ listeningCanceller = delegate.executeDelayed({ setListeningState() }, 0L)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index b07b1a9..2dc82dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -31,6 +31,7 @@
import android.graphics.Rect;
import android.media.AudioManager;
import android.os.Handler;
+import android.os.Looper;
import android.provider.AlarmClock;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
@@ -46,7 +47,9 @@
import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -55,6 +58,7 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
+import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
@@ -63,6 +67,10 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.privacy.PrivacyChipEvent;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.qs.carrier.QSCarrierGroup;
import com.android.systemui.statusbar.CommandQueue;
@@ -101,7 +109,6 @@
private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
- private final Handler mHandler = new Handler();
private final NextAlarmController mAlarmController;
private final ZenModeController mZenController;
private final StatusBarIconController mStatusBarIconController;
@@ -140,9 +147,14 @@
private View mRingerContainer;
private Clock mClockView;
private DateView mDateView;
+ private OngoingPrivacyChip mPrivacyChip;
+ private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
private RingerModeTracker mRingerModeTracker;
+ private boolean mPermissionsHubEnabled;
+ private PrivacyItemController mPrivacyItemController;
+ private final UiEventLogger mUiEventLogger;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -156,22 +168,43 @@
private int mCutOutPaddingRight;
private float mExpandedHeaderAlpha = 1.0f;
private float mKeyguardExpansionFraction;
+ private boolean mPrivacyChipLogged = false;
+
+ private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
+ @Override
+ public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ mPrivacyChip.setPrivacyList(privacyItems);
+ setChipVisibility(!privacyItems.isEmpty());
+ }
+
+ @Override
+ public void onFlagChanged(boolean flag) {
+ if (mPermissionsHubEnabled != flag) {
+ StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
+ iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+ setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+ }
+ }
+ };
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
NextAlarmController nextAlarmController, ZenModeController zenModeController,
StatusBarIconController statusBarIconController,
- ActivityStarter activityStarter,
- CommandQueue commandQueue, RingerModeTracker ringerModeTracker) {
+ ActivityStarter activityStarter, PrivacyItemController privacyItemController,
+ CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
+ UiEventLogger uiEventLogger) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
mStatusBarIconController = statusBarIconController;
mActivityStarter = activityStarter;
+ mPrivacyItemController = privacyItemController;
mDualToneHandler = new DualToneHandler(
new ContextThemeWrapper(context, R.style.QSHeaderTheme));
mCommandQueue = commandQueue;
mRingerModeTracker = ringerModeTracker;
+ mUiEventLogger = uiEventLogger;
}
@Override
@@ -198,8 +231,11 @@
mRingerModeTextView = findViewById(R.id.ringer_mode_text);
mRingerContainer = findViewById(R.id.ringer_container);
mRingerContainer.setOnClickListener(this::onClick);
+ mPrivacyChip = findViewById(R.id.privacy_chip);
+ mPrivacyChip.setOnClickListener(this::onClick);
mCarrierGroup = findViewById(R.id.carrier_group);
+
updateResources();
Rect tintArea = new Rect(0, 0, 0, 0);
@@ -219,6 +255,7 @@
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
mDateView = findViewById(R.id.date);
+ mSpace = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -229,6 +266,8 @@
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
mRingerModeTextView.setSelected(true);
mNextAlarmTextView.setSelected(true);
+
+ mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
}
public QuickQSPanel getHeaderQsPanel() {
@@ -241,6 +280,10 @@
com.android.internal.R.string.status_bar_camera));
ignored.add(mContext.getResources().getString(
com.android.internal.R.string.status_bar_microphone));
+ if (mPermissionsHubEnabled) {
+ ignored.add(mContext.getResources().getString(
+ com.android.internal.R.string.status_bar_location));
+ }
return ignored;
}
@@ -256,6 +299,20 @@
}
}
+ private void setChipVisibility(boolean chipVisible) {
+ if (chipVisible && mPermissionsHubEnabled) {
+ mPrivacyChip.setVisibility(View.VISIBLE);
+ // Makes sure that the chip is logged as viewed at most once each time QS is opened
+ // mListening makes sure that the callback didn't return after the user closed QS
+ if (!mPrivacyChipLogged && mListening) {
+ mPrivacyChipLogged = true;
+ mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
+ }
+ } else {
+ mPrivacyChip.setVisibility(View.GONE);
+ }
+ }
+
private boolean updateRingerStatus() {
boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -363,6 +420,7 @@
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
+ updatePrivacyChipAlphaAnimator();
}
private void updateStatusIconAlphaAnimator() {
@@ -377,6 +435,12 @@
.build();
}
+ private void updatePrivacyChipAlphaAnimator() {
+ mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
+ .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
+ .build();
+ }
+
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -415,6 +479,10 @@
mHeaderTextContainerView.setVisibility(INVISIBLE);
}
}
+ if (mPrivacyChipAlphaAnimator != null) {
+ mPrivacyChip.setExpanded(expansionFraction > 0.5);
+ mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
+ }
if (expansionFraction < 1 && expansionFraction > 0.99) {
if (mHeaderQsPanel.switchTileLayout()) {
updateResources();
@@ -453,6 +521,31 @@
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
+ if (padding == null) {
+ mSystemIconsView.setPaddingRelative(
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
+ } else {
+ mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
+
+ }
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+ boolean cornerCutout = cornerCutoutPadding != null
+ && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
+ if (cutout != null) {
+ Rect topCutout = cutout.getBoundingRectTop();
+ if (topCutout.isEmpty() || cornerCutout) {
+ mHasTopCutout = false;
+ lp.width = 0;
+ mSpace.setVisibility(View.GONE);
+ } else {
+ mHasTopCutout = true;
+ lp.width = topCutout.width();
+ mSpace.setVisibility(View.VISIBLE);
+ }
+ }
+ mSpace.setLayoutParams(lp);
+ setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -513,10 +606,15 @@
mZenController.addCallback(this);
mAlarmController.addCallback(this);
mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
+ // Get the most up to date info
+ mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+ mPrivacyItemController.addCallback(mPICCallback);
} else {
mZenController.removeCallback(this);
mAlarmController.removeCallback(this);
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+ mPrivacyItemController.removeCallback(mPICCallback);
+ mPrivacyChipLogged = false;
}
}
@@ -534,6 +632,15 @@
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
}
+ } else if (v == mPrivacyChip) {
+ // If the privacy chip is visible, it means there were some indicators
+ Handler mUiHandler = new Handler(Looper.getMainLooper());
+ mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
+ mUiHandler.post(() -> {
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
+ mHost.collapsePanels();
+ });
} else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Settings.ACTION_SOUND_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9115b48..d03082e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -809,7 +809,9 @@
@Override
public void addCallback(OverviewProxyListener listener) {
- mConnectionCallbacks.add(listener);
+ if (!mConnectionCallbacks.contains(listener)) {
+ mConnectionCallbacks.add(listener);
+ }
listener.onConnectionChanged(mOverviewProxy != null);
listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
new file mode 100644
index 0000000..3fd7f945
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_EDIT;
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.inject.Inject;
+
+/**
+ * Receiver to proxy the share or edit intent, used to clean up the notification and send
+ * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
+ */
+public class ActionProxyReceiver extends BroadcastReceiver {
+ private static final String TAG = "ActionProxyReceiver";
+
+ private static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
+ private final StatusBar mStatusBar;
+ private final ActivityManagerWrapper mActivityManagerWrapper;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+
+ @Inject
+ public ActionProxyReceiver(Optional<StatusBar> statusBar,
+ ActivityManagerWrapper activityManagerWrapper,
+ ScreenshotSmartActions screenshotSmartActions) {
+ mStatusBar = statusBar.orElse(null);
+ mActivityManagerWrapper = activityManagerWrapper;
+ mScreenshotSmartActions = screenshotSmartActions;
+ }
+
+ @Override
+ public void onReceive(Context context, final Intent intent) {
+ Runnable startActivityRunnable = () -> {
+ try {
+ mActivityManagerWrapper.closeSystemWindows(
+ SYSTEM_DIALOG_REASON_SCREENSHOT).get(
+ CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Unable to share screenshot", e);
+ return;
+ }
+
+ PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setDisallowEnterPictureInPictureWhileLaunching(
+ intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
+ try {
+ actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled", e);
+ }
+
+ };
+
+ if (mStatusBar != null) {
+ mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+ true /* dismissShade */, true /* afterKeyguardGone */,
+ true /* deferred */);
+ } else {
+ startActivityRunnable.run();
+ }
+
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
+ ? ACTION_TYPE_EDIT
+ : ACTION_TYPE_SHARE;
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), actionType, false);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
deleted file mode 100644
index 8c48655..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.AsyncTask;
-
-/**
- * An AsyncTask that deletes an image from the media store in the background.
- */
-class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
- private Context mContext;
-
- DeleteImageInBackgroundTask(Context context) {
- mContext = context;
- }
-
- @Override
- protected Void doInBackground(Uri... params) {
- if (params.length != 1) return null;
-
- Uri screenshotUri = params[0];
- ContentResolver resolver = mContext.getContentResolver();
- resolver.delete(screenshotUri, null, null);
- return null;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
new file mode 100644
index 0000000..9028bb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import com.android.systemui.dagger.qualifiers.Background;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Removes the file at a provided URI.
+ */
+public class DeleteScreenshotReceiver extends BroadcastReceiver {
+
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+ private final Executor mBackgroundExecutor;
+
+ @Inject
+ public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
+ @Background Executor backgroundExecutor) {
+ mScreenshotSmartActions = screenshotSmartActions;
+ mBackgroundExecutor = backgroundExecutor;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
+ return;
+ }
+
+ // And delete the image from the media store
+ final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
+ mBackgroundExecutor.execute(() -> {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.delete(uri, null, null);
+ });
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 4a4aa79..c535230 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -21,8 +21,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -30,13 +28,10 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -62,7 +57,6 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
-import android.util.Slog;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -87,23 +81,15 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
-import dagger.Lazy;
-
/**
* Class for handling device screen shots
*/
@@ -192,6 +178,7 @@
private final UiEventLogger mUiEventLogger;
private final Context mContext;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private final Display mDisplay;
@@ -247,9 +234,11 @@
@Inject
public GlobalScreenshot(
Context context, @Main Resources resources,
+ ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController screenshotNotificationsController,
UiEventLogger uiEventLogger) {
mContext = context;
+ mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mUiEventLogger = uiEventLogger;
@@ -504,7 +493,7 @@
});
mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- dismissScreenshot("back pressed", true);
+ dismissScreenshot("back pressed", false);
return true;
}
return false;
@@ -704,7 +693,7 @@
});
}
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
mSaveInBgTask.execute();
}
@@ -1024,6 +1013,7 @@
mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
mScreenshotPreview.setContentDescription(
mContext.getResources().getString(R.string.screenshot_preview_description));
+ mScreenshotPreview.setOnClickListener(null);
mScreenshotLayout.setAlpha(1);
mDismissButton.setTranslationY(0);
mActionsContainer.setTranslationY(0);
@@ -1116,119 +1106,4 @@
return insetDrawable;
}
}
-
- /**
- * Receiver to proxy the share or edit intent, used to clean up the notification and send
- * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
- */
- public static class ActionProxyReceiver extends BroadcastReceiver {
- static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
- private final StatusBar mStatusBar;
-
- @Inject
- public ActionProxyReceiver(Optional<Lazy<StatusBar>> statusBarLazy) {
- Lazy<StatusBar> statusBar = statusBarLazy.orElse(null);
- mStatusBar = statusBar != null ? statusBar.get() : null;
- }
-
- @Override
- public void onReceive(Context context, final Intent intent) {
- Runnable startActivityRunnable = () -> {
- try {
- ActivityManagerWrapper.getInstance().closeSystemWindows(
- SYSTEM_DIALOG_REASON_SCREENSHOT).get(
- CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException | InterruptedException | ExecutionException e) {
- Slog.e(TAG, "Unable to share screenshot", e);
- return;
- }
-
- PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
- }
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setDisallowEnterPictureInPictureWhileLaunching(
- intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
- try {
- actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled", e);
- }
-
- };
-
- if (mStatusBar != null) {
- mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
- true /* dismissShade */, true /* afterKeyguardGone */,
- true /* deferred */);
- } else {
- startActivityRunnable.run();
- }
-
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
- ? ACTION_TYPE_EDIT
- : ACTION_TYPE_SHARE;
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, false);
- }
- }
- }
-
- /**
- * Removes the notification for a screenshot after a share target is chosen.
- */
- public static class TargetChosenReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Clear the notification only after the user has chosen a share action
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
- }
- }
-
- /**
- * Removes the last screenshot.
- */
- public static class DeleteScreenshotReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
- return;
- }
-
- // Clear the notification when the image is deleted
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
-
- // And delete the image from the media store
- final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
- new DeleteImageInBackgroundTask(context).execute(uri);
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
- }
- }
- }
-
- /**
- * Executes the smart action tapped by the user in the notification.
- */
- public static class SmartActionsReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
- Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
- ActivityOptions opts = ActivityOptions.makeBasic();
-
- try {
- pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled", e);
- }
-
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, true);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 468b9b1..df1d789 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -81,6 +81,7 @@
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
private final Context mContext;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
private final GlobalScreenshot.SaveImageInBackgroundData mParams;
private final GlobalScreenshot.SavedImageData mImageData;
private final String mImageFileName;
@@ -90,8 +91,10 @@
private final boolean mSmartActionsEnabled;
private final Random mRandom = new Random();
- SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
+ SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
+ GlobalScreenshot.SaveImageInBackgroundData data) {
mContext = context;
+ mScreenshotSmartActions = screenshotSmartActions;
mImageData = new GlobalScreenshot.SavedImageData();
// Prepare all the output metadata
@@ -141,7 +144,7 @@
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
mScreenshotId, uri, image, mSmartActionsProvider,
mSmartActionsEnabled, getUserHandle(mContext));
@@ -199,7 +202,7 @@
SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
1000);
smartActions.addAll(buildSmartActions(
- ScreenshotSmartActions.getSmartActions(
+ mScreenshotSmartActions.getSmartActions(
mScreenshotId, smartActionsFuture, timeoutMs,
mSmartActionsProvider),
mContext));
@@ -274,11 +277,8 @@
// by setting the (otherwise unused) request code to the current user id.
int requestCode = context.getUserId();
- PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
Intent sharingChooserIntent =
- Intent.createChooser(sharingIntent, null, chooserAction.getIntentSender())
+ Intent.createChooser(sharingIntent, null)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -288,7 +288,7 @@
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ new Intent(context, ActionProxyReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
.putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
@@ -333,10 +333,8 @@
// Create a edit action
PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ new Intent(context, ActionProxyReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
- .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
- editIntent.getComponent() != null)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
@@ -358,7 +356,7 @@
// Create a delete action for the notification
PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+ new Intent(context, DeleteScreenshotReceiver.class)
.putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
@@ -398,7 +396,7 @@
String actionType = extras.getString(
ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
- Intent intent = new Intent(context, GlobalScreenshot.SmartActionsReceiver.class)
+ Intent intent = new Intent(context, SmartActionsReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 442b373..633cdd6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -39,14 +39,21 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Collects the static functions for retrieving and acting on smart actions.
*/
+@Singleton
public class ScreenshotSmartActions {
private static final String TAG = "ScreenshotSmartActions";
+ @Inject
+ public ScreenshotSmartActions() {}
+
@VisibleForTesting
- static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
+ CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
String screenshotId, Uri screenshotUri, Bitmap image,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
boolean smartActionsEnabled, UserHandle userHandle) {
@@ -86,7 +93,7 @@
}
@VisibleForTesting
- static List<Notification.Action> getSmartActions(String screenshotId,
+ List<Notification.Action> getSmartActions(String screenshotId,
CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
long startTimeMs = SystemClock.uptimeMillis();
@@ -116,7 +123,7 @@
}
}
- static void notifyScreenshotOp(String screenshotId,
+ void notifyScreenshotOp(String screenshotId,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
@@ -127,7 +134,7 @@
}
}
- static void notifyScreenshotAction(Context context, String screenshotId, String action,
+ void notifyScreenshotAction(Context context, String screenshotId, String action,
boolean isSmartAction) {
try {
ScreenshotNotificationSmartActionsProvider provider =
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
new file mode 100644
index 0000000..217235b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.util.Slog;
+
+import javax.inject.Inject;
+
+
+/**
+ * Executes the smart action tapped by the user in the notification.
+ */
+public class SmartActionsReceiver extends BroadcastReceiver {
+ private static final String TAG = "SmartActionsReceiver";
+
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+
+ @Inject
+ SmartActionsReceiver(ScreenshotSmartActions screenshotSmartActions) {
+ mScreenshotSmartActions = screenshotSmartActions;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
+ Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
+ ActivityOptions opts = ActivityOptions.makeBasic();
+
+ try {
+ pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled", e);
+ }
+
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), actionType, true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 6f143da..a043f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -61,7 +61,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
- mScreenshot.dismissScreenshot("close system dialogs", true);
+ mScreenshot.dismissScreenshot("close system dialogs", false);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 8c24c54..2638d28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -154,7 +154,7 @@
Dependency.get(IWindowManager.class));
navBar.setAutoHideController(autoHideController);
navBar.restoreAppearanceAndTransientState();
- mNavigationBars.append(displayId, navBar);
+ mNavigationBars.put(displayId, navBar);
if (result != null) {
navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 9abc660..aba9e10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,18 +24,24 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
@@ -52,6 +58,7 @@
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
@@ -127,6 +134,8 @@
private final NotificationEntryManagerLogger mLogger;
+ private final IStatusBarService mStatusBarService;
+
// Lazily retrieved dependencies
private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
@@ -138,6 +147,8 @@
private final NotificationRankingManager mRankingManager;
private final FeatureFlags mFeatureFlags;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
+ private final HeadsUpManager mHeadsUpManager;
+ private final StatusBarStateController mStatusBarStateController;
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
@@ -201,7 +212,10 @@
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController) {
+ ForegroundServiceDismissalFeatureController fgsFeatureController,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController
+ ) {
mLogger = logger;
mGroupManager = groupManager;
mRankingManager = rankingManager;
@@ -211,6 +225,11 @@
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
mFgsFeatureController = fgsFeatureController;
+ mHeadsUpManager = headsUpManager;
+ mStatusBarStateController = statusBarStateController;
+
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -496,6 +515,9 @@
removedByUser |= entryDismissed;
mLogger.logNotifRemoved(entry.getKey(), removedByUser);
+ if (removedByUser && visibility != null) {
+ sendNotificationRemovalToServer(entry.getKey(), entry.getSbn(), visibility);
+ }
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryRemoved(entry, visibility, removedByUser, reason);
}
@@ -511,6 +533,36 @@
}
}
+ private void sendNotificationRemovalToServer(
+ String key,
+ StatusBarNotification notification,
+ NotificationVisibility nv) {
+ final String pkg = notification.getPackageName();
+ final String tag = notification.getTag();
+ final int id = notification.getId();
+ final int userId = notification.getUser().getIdentifier();
+ try {
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (mHeadsUpManager.isAlerting(key)) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStatusBarStateController.isDozing()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
+ int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+ mStatusBarService.onNotificationClear(
+ pkg,
+ tag,
+ id,
+ userId,
+ notification.getKey(),
+ dismissalSurface,
+ dismissalSentiment,
+ nv);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ }
+
/**
* Ensures that the group children are cancelled immediately when the group summary is cancelled
* instead of waiting for the notification manager to send all cancels. Otherwise this could
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 2747696..f982cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -382,7 +382,7 @@
}
private fun shouldAnimateVisibility() =
- dozeParameters.getAlwaysOn() && !dozeParameters.getDisplayNeedsBlanking()
+ dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
interface WakeUpListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index d2c202c..d661b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import java.util.concurrent.Executor;
@@ -88,7 +89,9 @@
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController) {
+ ForegroundServiceDismissalFeatureController fgsFeatureController,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController) {
return new NotificationEntryManager(
logger,
groupManager,
@@ -98,7 +101,9 @@
notificationRowBinderLazy,
notificationRemoteInputManagerLazy,
leakDetector,
- fgsFeatureController);
+ fgsFeatureController,
+ headsUpManager,
+ statusBarStateController);
}
/** Provides an instance of {@link NotificationGutsManager} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index bd0d0b3..4441270 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -21,7 +21,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -44,7 +43,6 @@
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.Collection;
import java.util.Collections;
@@ -74,7 +72,6 @@
private final Executor mUiBgExecutor;
private final NotificationEntryManager mEntryManager;
private final NotificationPanelLogger mNotificationPanelLogger;
- private HeadsUpManager mHeadsUpManager;
private final ExpansionStateLogger mExpansionStateLogger;
protected Handler mHandler = new Handler();
@@ -226,9 +223,6 @@
NotificationVisibility visibility,
boolean removedByUser,
int reason) {
- if (removedByUser && visibility != null) {
- logNotificationClear(entry.getKey(), entry.getSbn(), visibility);
- }
mExpansionStateLogger.onEntryRemoved(entry.getKey());
}
@@ -250,10 +244,6 @@
mListContainer = listContainer;
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
public void stopNotificationLogging() {
if (mLogging) {
mLogging = false;
@@ -296,30 +286,6 @@
}
}
- // TODO: This method has side effects, it is NOT just logging that a notification
- // was cleared, it also actually removes the notification
- private void logNotificationClear(String key, StatusBarNotification notification,
- NotificationVisibility nv) {
- final String pkg = notification.getPackageName();
- final String tag = notification.getTag();
- final int id = notification.getId();
- final int userId = notification.getUserId();
- try {
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (mHeadsUpManager.isAlerting(key)) {
- dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mListContainer.hasPulsingNotifications()) {
- dismissalSurface = NotificationStats.DISMISSAL_AOD;
- }
- int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
- mBarService.onNotificationClear(pkg, tag, id, userId, notification.getKey(),
- dismissalSurface,
- dismissalSentiment, nv);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- }
-
/**
* Logs Notification inflation error
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 91cc896..ff7793d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -329,6 +329,7 @@
// shade.
for (i in parent.childCount - 1 downTo -1) {
val child: View? = parent.getChildAt(i)
+
child?.let {
logShadeChild(i, child)
// If this child is a header, update the tracked positions
@@ -342,7 +343,8 @@
}
}
- val row = child as? ExpandableNotificationRow
+ val row = (child as? ExpandableNotificationRow)
+ ?.takeUnless { it.visibility == View.GONE }
// Is there a section discontinuity? This usually occurs due to HUNs
inIncomingSection = inIncomingSection || nextBucket?.let { next ->
@@ -389,7 +391,7 @@
// Offset the target to account for the current position of the people header.
peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
- peopleState?.targetPosition?.let { target ->
+ peopleState.targetPosition?.let { target ->
if (current < target) target - 1 else target
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index f5ea1c8..5fab4be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -28,6 +28,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
import java.io.PrintWriter;
@@ -52,6 +53,7 @@
private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
private final Resources mResources;
+ private final BatteryController mBatteryController;
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
@@ -62,10 +64,12 @@
AmbientDisplayConfiguration ambientDisplayConfiguration,
AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
PowerManager powerManager,
+ BatteryController batteryController,
TunerService tunerService) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
+ mBatteryController = batteryController;
mControlScreenOffAnimation = !getDisplayNeedsBlanking();
mPowerManager = powerManager;
@@ -164,7 +168,7 @@
* @return {@code true} if enabled and available.
*/
public boolean getAlwaysOn() {
- return mDozeAlwaysOn;
+ return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index a35aca5..9606318 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -121,6 +121,7 @@
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
+ private final SysUiState mSysUiState;
private final Runnable mStateChangeCallback;
private final PluginManager mPluginManager;
@@ -197,14 +198,22 @@
}
};
+ private final SysUiState.SysUiStateCallback mSysUiStateCallback =
+ new SysUiState.SysUiStateCallback() {
+ @Override
+ public void onSystemUiStateChanged(int sysUiFlags) {
+ mSysUiFlags = sysUiFlags;
+ }
+ };
+
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiFlagContainer, PluginManager pluginManager,
- Runnable stateChangeCallback) {
+ SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
super(Dependency.get(BroadcastDispatcher.class));
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
mOverviewProxyService = overviewProxyService;
+ mSysUiState = sysUiState;
mPluginManager = pluginManager;
mStateChangeCallback = stateChangeCallback;
ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -238,7 +247,6 @@
mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
updateCurrentUserResources();
- sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
public void updateCurrentUserResources() {
@@ -287,6 +295,7 @@
mIsAttached = true;
Dependency.get(ProtoTracer.class).add(this);
mOverviewProxyService.addCallback(mQuickSwitchListener);
+ mSysUiState.addCallback(mSysUiStateCallback);
updateIsEnabled();
startTracking();
}
@@ -298,6 +307,7 @@
mIsAttached = false;
Dependency.get(ProtoTracer.class).remove(this);
mOverviewProxyService.removeCallback(mQuickSwitchListener);
+ mSysUiState.removeCallback(mSysUiStateCallback);
updateIsEnabled();
stopTracking();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b7733cc..f43fa64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -120,6 +120,7 @@
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -131,6 +132,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
@@ -354,6 +356,7 @@
// If the button will actually become visible and the navbar is about to hide,
// tell the statusbar to keep it around for longer
mAutoHideController.touchAutoHide();
+ mNavigationBarView.notifyActiveTouchRegions();
}
};
@@ -550,6 +553,9 @@
mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
}
+ mHandler.removeCallbacks(mAutoDim);
+ mNavigationBarView = null;
+ mOrientationHandle = null;
}
@Override
@@ -1458,11 +1464,11 @@
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
- final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
- .create(NavigationBarFragment.class);
navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
+ final NavigationBarFragment fragment =
+ FragmentHostManager.get(v).create(NavigationBarFragment.class);
final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
fragmentHost.getFragmentManager().beginTransaction()
.replace(R.id.navigation_bar_frame, fragment, TAG)
@@ -1472,6 +1478,8 @@
@Override
public void onViewDetachedFromWindow(View v) {
+ final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+ fragmentHost.removeTagListener(TAG, listener);
FragmentHostManager.removeAndDestroy(v);
navigationBarView.removeOnAttachStateChangeListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7936e53..84512ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -115,12 +115,8 @@
int mNavigationIconHints = 0;
private int mNavBarMode;
- private Rect mHomeButtonBounds = new Rect();
- private Rect mBackButtonBounds = new Rect();
- private Rect mRecentsButtonBounds = new Rect();
- private Rect mRotationButtonBounds = new Rect();
private final Region mActiveRegion = new Region();
- private int[] mTmpPosition = new int[2];
+ private Rect mTmpBounds = new Rect();
private KeyButtonDrawable mBackIcon;
private KeyButtonDrawable mHomeDefaultIcon;
@@ -712,6 +708,7 @@
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
+ notifyActiveTouchRegions();
}
@VisibleForTesting
@@ -929,42 +926,30 @@
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- mActiveRegion.setEmpty();
- updateButtonLocation(getBackButton(), mBackButtonBounds, true);
- updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
- updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
- updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
- // TODO: Handle button visibility changes
- mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
+ notifyActiveTouchRegions();
mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
}
- private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
- boolean isActive) {
+ /**
+ * Notifies the overview service of the active touch regions.
+ */
+ public void notifyActiveTouchRegions() {
+ mActiveRegion.setEmpty();
+ updateButtonLocation(getBackButton());
+ updateButtonLocation(getHomeButton());
+ updateButtonLocation(getRecentsButton());
+ updateButtonLocation(getRotateSuggestionButton());
+ mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
+ }
+
+ private void updateButtonLocation(ButtonDispatcher button) {
View view = button.getCurrentView();
- if (view == null) {
- buttonBounds.setEmpty();
+ if (view == null || !button.isVisible()) {
return;
}
- // Temporarily reset the translation back to origin to get the position in window
- final float posX = view.getTranslationX();
- final float posY = view.getTranslationY();
- view.setTranslationX(0);
- view.setTranslationY(0);
- if (isActive) {
- view.getLocationOnScreen(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- mActiveRegion.op(buttonBounds, Op.UNION);
- }
- view.getLocationInWindow(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- view.setTranslationX(posX);
- view.setTranslationY(posY);
+ view.getBoundsOnScreen(mTmpBounds);
+ mActiveRegion.op(mTmpBounds, Op.UNION);
}
private void updateOrientationViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5bb8fab..b2cfcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -47,6 +47,9 @@
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyType;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.screenrecord.RecordingController;
@@ -70,6 +73,9 @@
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.time.DateFormatUtil;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
@@ -87,13 +93,13 @@
ZenModeController.Callback,
DeviceProvisionedListener,
KeyguardStateController.Callback,
+ PrivacyItemController.Callback,
LocationController.LocationChangeCallback,
RecordingController.RecordingStateChangeCallback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final int LOCATION_STATUS_ICON_ID =
- com.android.internal.R.drawable.perm_group_location;
+ static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
private final String mSlotCast;
private final String mSlotHotspot;
@@ -107,6 +113,8 @@
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
+ private final String mSlotMicrophone;
+ private final String mSlotCamera;
private final String mSlotSensorsOff;
private final String mSlotScreenRecord;
private final int mDisplayId;
@@ -132,6 +140,7 @@
private final DeviceProvisionedController mProvisionedController;
private final KeyguardStateController mKeyguardStateController;
private final LocationController mLocationController;
+ private final PrivacyItemController mPrivacyItemController;
private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
private final RecordingController mRecordingController;
@@ -162,7 +171,8 @@
RecordingController recordingController,
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
@Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
- RingerModeTracker ringerModeTracker) {
+ RingerModeTracker ringerModeTracker,
+ PrivacyItemController privacyItemController) {
mIconController = iconController;
mCommandQueue = commandQueue;
mBroadcastDispatcher = broadcastDispatcher;
@@ -181,6 +191,7 @@
mProvisionedController = deviceProvisionedController;
mKeyguardStateController = keyguardStateController;
mLocationController = locationController;
+ mPrivacyItemController = privacyItemController;
mSensorPrivacyController = sensorPrivacyController;
mRecordingController = recordingController;
mUiBgExecutor = uiBgExecutor;
@@ -200,6 +211,8 @@
mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver);
mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location);
+ mSlotMicrophone = resources.getString(com.android.internal.R.string.status_bar_microphone);
+ mSlotCamera = resources.getString(com.android.internal.R.string.status_bar_camera);
mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off);
mSlotScreenRecord = resources.getString(
com.android.internal.R.string.status_bar_screen_record);
@@ -271,6 +284,13 @@
mResources.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
+ // privacy items
+ mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
+ mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()));
+ mIconController.setIconVisibility(mSlotMicrophone, false);
+ mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
+ mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()));
+ mIconController.setIconVisibility(mSlotCamera, false);
mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
mResources.getString(R.string.accessibility_location_active));
mIconController.setIconVisibility(mSlotLocation, false);
@@ -294,6 +314,7 @@
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardStateController.addCallback(this);
+ mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
mRecordingController.addCallback(this);
@@ -609,9 +630,44 @@
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
+ @Override // PrivacyItemController.Callback
+ public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ updatePrivacyItems(privacyItems);
+ }
+
+ private void updatePrivacyItems(List<PrivacyItem> items) {
+ boolean showCamera = false;
+ boolean showMicrophone = false;
+ boolean showLocation = false;
+ for (PrivacyItem item : items) {
+ if (item == null /* b/124234367 */) {
+ Log.e(TAG, "updatePrivacyItems - null item found");
+ StringWriter out = new StringWriter();
+ mPrivacyItemController.dump(null, new PrintWriter(out), null);
+ // Throw so we can look into this
+ throw new NullPointerException(out.toString());
+ }
+ switch (item.getPrivacyType()) {
+ case TYPE_CAMERA:
+ showCamera = true;
+ break;
+ case TYPE_LOCATION:
+ showLocation = true;
+ break;
+ case TYPE_MICROPHONE:
+ showMicrophone = true;
+ break;
+ }
+ }
+
+ mIconController.setIconVisibility(mSlotCamera, showCamera);
+ mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
+ mIconController.setIconVisibility(mSlotLocation, showLocation);
+ }
+
@Override
public void onLocationActiveChanged(boolean active) {
- updateLocation();
+ if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation();
}
// Updates the status view based on the current state of location requests.
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 c5acd9b..c5571e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -18,9 +18,6 @@
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_CLOCK;
-import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -1101,7 +1098,6 @@
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);
@@ -4175,7 +4171,6 @@
@Override
public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
mTopHidesStatusBar = topAppHidesStatusBar;
- updateStatusBarIcons(topAppHidesStatusBar);
if (!topAppHidesStatusBar && mWereIconsJustHidden) {
// Immediately update the icon hidden state, since that should only apply if we're
// staying fullscreen.
@@ -4185,17 +4180,6 @@
updateHideIconsForBouncer(true /* animate */);
}
- private void updateStatusBarIcons(boolean topAppHidesStatusBar) {
- int flags1 = StatusBarManager.DISABLE_NONE;
- int flags2 = StatusBarManager.DISABLE2_NONE;
- if (topAppHidesStatusBar) {
- flags1 = DISABLE_NOTIFICATION_ICONS | DISABLE_CLOCK;
- flags2 = DISABLE2_SYSTEM_ICONS;
- }
-
- mCommandQueue.disable(mDisplayId, flags1, flags2, false);
- }
-
protected void toggleKeyboardShortcuts(int deviceId) {
KeyboardShortcuts.toggle(mContext, deviceId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 6dd96f92..9560195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -23,6 +23,7 @@
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.icu.text.DateTimePatternGenerator;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
@@ -53,8 +54,6 @@
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import libcore.icu.LocaleData;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
@@ -391,15 +390,16 @@
private final CharSequence getSmallTime() {
Context context = getContext();
boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId);
- LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ context.getResources().getConfiguration().locale);
final char MAGIC1 = '\uEF00';
final char MAGIC2 = '\uEF01';
SimpleDateFormat sdf;
String format = mShowSeconds
- ? is24 ? d.timeFormat_Hms : d.timeFormat_hms
- : is24 ? d.timeFormat_Hm : d.timeFormat_hm;
+ ? is24 ? dtpg.getBestPattern("Hms") : dtpg.getBestPattern("hms")
+ : is24 ? dtpg.getBestPattern("Hm") : dtpg.getBestPattern("hm");
if (!format.equals(mClockFormatString)) {
mContentDescriptionFormat = new SimpleDateFormat(format);
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 251693e..adfc14e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -39,6 +39,7 @@
import com.android.systemui.appops.AppOpItem;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.Utils;
@@ -65,8 +66,8 @@
@Inject
public LocationControllerImpl(Context context, AppOpsController appOpsController,
- @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher,
- BootCompleteCache bootCompleteCache) {
+ @Main Looper mainLooper, @Background Handler backgroundHandler,
+ BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) {
mContext = context;
mAppOpsController = appOpsController;
mBootCompleteCache = bootCompleteCache;
@@ -80,7 +81,7 @@
mAppOpsController.addCallback(new int[]{OP_MONITOR_HIGH_POWER_LOCATION}, this);
// Examine the current location state and initialize the status view.
- updateActiveLocationRequests();
+ backgroundHandler.post(this::updateActiveLocationRequests);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
index 2c8297c..dce38c1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
@@ -16,8 +16,6 @@
package com.android.systemui.tv;
-import android.content.Context;
-
import com.android.systemui.dagger.DefaultComponentBinder;
import com.android.systemui.dagger.DependencyBinder;
import com.android.systemui.dagger.DependencyProvider;
@@ -30,7 +28,6 @@
import javax.inject.Singleton;
-import dagger.BindsInstance;
import dagger.Component;
/**
@@ -52,9 +49,7 @@
* Component Builder interface. This allows to bind Context instance in the component
*/
@Component.Builder
- interface Builder {
- @BindsInstance Builder context(Context context);
-
+ interface Builder extends SystemUIRootComponent.Builder {
TvSystemUIRootComponent build();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 7561af7..b1241b1 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -70,6 +70,8 @@
if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+ mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
}
Intent intent = getIntent();
@@ -119,6 +121,7 @@
}
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
if (!connected) {
+ Log.d(TAG, "USB disconnected, notifying service");
notifyService(false);
mActivity.finish();
}
@@ -126,29 +129,20 @@
}
@Override
- public void onStart() {
- super.onStart();
- if (mDisconnectedReceiver != null) {
- IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
- mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
- }
- }
-
- @Override
- protected void onStop() {
+ protected void onDestroy() {
if (mDisconnectedReceiver != null) {
mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
}
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- // If the ADB service has not yet been notified due to this dialog being closed in some
- // other way then notify the service to deny the connection to ensure system_server sends
- // a response to adbd.
- if (!mServiceNotified) {
- notifyService(false);
+ // Only notify the service if the activity is finishing; if onDestroy has been called due to
+ // a configuration change then allow the user to still authorize the connection the next
+ // time the activity is in the foreground.
+ if (isFinishing()) {
+ // If the ADB service has not yet been notified due to this dialog being closed in some
+ // other way then notify the service to deny the connection to ensure system_server
+ // sends a response to adbd.
+ if (!mServiceNotified) {
+ notifyService(false);
+ }
}
super.onDestroy();
}
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
similarity index 66%
rename from core/java/android/app/IRequestFinishCallback.aidl
rename to packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
index 3270565..84ab66b 100644
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package android.app;
+package com.android.systemui.util.settings;
/**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
+ * Public interface that can be injected to interact with Settings.Global.
*
- * {@hide}
+ * See {@link SettingsProxy} for details.
*/
-oneway interface IRequestFinishCallback {
- void requestFinish();
+public interface GlobalSettings extends SettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
new file mode 100644
index 0000000..1a30b0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class GlobalSettingsImpl implements GlobalSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ GlobalSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.Global.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.Global.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.System and Settings.Secure");
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.Global.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ return Settings.Global.putStringForUser(
+ mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault);
+ }
+}
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
similarity index 66%
copy from core/java/android/app/IRequestFinishCallback.aidl
copy to packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
index 3270565..798033e 100644
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package android.app;
+package com.android.systemui.util.settings;
/**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
+ * Public interface that can be injected to interact with Settings.Secure.
*
- * {@hide}
+ * See {@link SettingsProxy} for details.
*/
-oneway interface IRequestFinishCallback {
- void requestFinish();
+
+public interface SecureSettings extends SettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
new file mode 100644
index 0000000..020c234
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SecureSettingsImpl implements SecureSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ SecureSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.Secure.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.Secure.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return Settings.Secure.putString(mContentResolver, name, value, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.Secure.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ return Settings.Secure.putStringForUser(
+ mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return Settings.Secure.putString(mContentResolver, name, value, tag, makeDefault);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
new file mode 100644
index 0000000..5c37f79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Used to interact with Settings.Secure, Settings.Global, and Settings.System.
+ *
+ * This interface can be implemented to give instance method (instead of static method) versions
+ * of Settings.Secure, Settings.Global, and Settings.System. It can be injected into class
+ * constructors and then faked or mocked as needed in tests.
+ *
+ * You can ask for {@link SecureSettings}, {@link GlobalSettings}, or {@link SystemSettings} to be
+ * injected as needed.
+ *
+ * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
+ * normally found on {@link ContentResolver} instances, unifying setting related actions in one
+ * place.
+ */
+public interface SettingsProxy {
+
+ /**
+ * Returns the {@link ContentResolver} this instance was constructed with.
+ */
+ ContentResolver getContentResolver();
+
+ /**
+ * Returns the user id for the associated {@link ContentResolver}.
+ */
+ default int getUserId() {
+ return getContentResolver().getUserId();
+ }
+
+ /**
+ * Construct the content URI for a particular name/value pair,
+ * useful for monitoring changes with a ContentObserver.
+ * @param name to look up in the table
+ * @return the corresponding content URI, or null if not present
+ */
+ Uri getUriFor(String name);
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserver(String name, ContentObserver settingsObserver) {
+ registerContentObserverForUser(name, settingsObserver, getUserId());
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserverForUser(
+ String name, ContentObserver settingsObserver, int userHandle) {
+ getContentResolver().registerContentObserver(
+ getUriFor(name), false, settingsObserver, userHandle);
+ }
+
+ /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
+ default void unregisterContentObserver(ContentObserver settingsObserver) {
+ getContentResolver().unregisterContentObserver(settingsObserver);
+ }
+
+ /**
+ * Look up a name in the database.
+ * @param name to look up in the table
+ * @return the corresponding value, or null if not present
+ */
+ default String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ /**See {@link #getString(String)}. */
+ String getStringForUser(String name, int userHandle);
+
+ /**
+ * Store a name/value pair into the database. Values written by this method will be
+ * overridden if a restore happens in the future.
+ *
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ boolean putString(String name, String value, boolean overrideableByRestore);
+
+ /**
+ * Store a name/value pair into the database.
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putString(String name, String value) {
+ return putStringForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(String name, String value, int userHandle);
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+
+ /**
+ * Store a name/value pair into the database.
+ * <p>
+ * The method takes an optional tag to associate with the setting
+ * which can be used to clear only settings made by your package and
+ * associated with this tag by passing the tag to {@link
+ * #resetToDefaults(String)}. Anyone can override
+ * the current tag. Also if another package changes the setting
+ * then the tag will be set to the one specified in the set call
+ * which can be null. Also any of the settings setters that do not
+ * take a tag as an argument effectively clears the tag.
+ * </p><p>
+ * For example, if you set settings A and B with tags T1 and T2 and
+ * another app changes setting A (potentially to the same value), it
+ * can assign to it a tag T3 (note that now the package that changed
+ * the setting is not yours). Now if you reset your changes for T1 and
+ * T2 only setting B will be reset and A not (as it was changed by
+ * another package) but since A did not change you are in the desired
+ * initial state. Now if the other app changes the value of A (assuming
+ * you registered an observer in the beginning) you would detect that
+ * the setting was changed by another app and handle this appropriately
+ * (ignore, set back to some value, etc).
+ * </p><p>
+ * Also the method takes an argument whether to make the value the
+ * default for this setting. If the system already specified a default
+ * value, then the one passed in here will <strong>not</strong>
+ * be set as the default.
+ * </p>
+ *
+ * @param name to store.
+ * @param value to associate with the name.
+ * @param tag to associate with the setting.
+ * @param makeDefault whether to make the value the default one.
+ * @return true if the value was set, false on database errors.
+ *
+ * @see #resetToDefaults(String)
+ *
+ */
+ boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault);
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you. The default value will be returned if the setting is
+ * not defined or not an integer.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid integer.
+ */
+ default int getInt(String name, int def) {
+ return getIntForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getInt(String, int)}. */
+ default int getIntForUser(String name, int def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Integer.parseInt(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ *
+ * @return The setting's current value.
+ */
+ default int getInt(String name) throws Settings.SettingNotFoundException {
+ return getIntForUser(name, getUserId());
+ }
+
+ /** See {@link #getInt(String)}. */
+ default int getIntForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as an
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putInt(String name, int value) {
+ return putIntForUser(name, value, getUserId());
+ }
+ /** See {@link #putInt(String, int)}. */
+ default boolean putIntForUser(String name, int value, int userHandle) {
+ return putStringForUser(name, Integer.toString(value), userHandle);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you. The default value will be returned if the setting is
+ * not defined or not a {@code long}.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid {@code long}.
+ */
+ default long getLong(String name, long def) {
+ return getLongForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getLong(String, long)}. */
+ default long getLongForUser(String name, long def, int userHandle) {
+ String valString = getStringForUser(name, userHandle);
+ long value;
+ try {
+ value = valString != null ? Long.parseLong(valString) : def;
+ } catch (NumberFormatException e) {
+ value = def;
+ }
+ return value;
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @return The setting's current value.
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ */
+ default long getLong(String name) throws Settings.SettingNotFoundException {
+ return getLongForUser(name, getUserId());
+ }
+
+ /** See {@link #getLong(String)}. */
+ default long getLongForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String valString = getStringForUser(name, userHandle);
+ try {
+ return Long.parseLong(valString);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a secure settings value as a long
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putLong(String name, long value) {
+ return putLongForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putLong(String, long)}. */
+ default boolean putLongForUser(String name, long value, int userHandle) {
+ return putStringForUser(name, Long.toString(value), userHandle);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a floating point number. Note that internally setting values are
+ * always stored as strings; this function converts the string to an
+ * float for you. The default value will be returned if the setting
+ * is not defined or not a valid float.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid float.
+ */
+ default float getFloat(String name, float def) {
+ return getFloatForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getFloat(String)}. */
+ default float getFloatForUser(String name, float def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Float.parseFloat(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a float. Note that internally setting values are always
+ * stored as strings; this function converts the string to a float
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not a float.
+ *
+ * @return The setting's current value.
+ */
+ default float getFloat(String name) throws Settings.SettingNotFoundException {
+ return getFloatForUser(name, getUserId());
+ }
+
+ /** See {@link #getFloat(String, float)}. */
+ default float getFloatForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ if (v == null) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ try {
+ return Float.parseFloat(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as a
+ * floating point number. This will either create a new entry in the
+ * table if the given name does not exist, or modify the value of the
+ * existing row with that name. Note that internally setting values
+ * are always stored as strings, so this function converts the given
+ * value to a string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putFloat(String name, float value) {
+ return putFloatForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putFloat(String, float)} */
+ default boolean putFloatForUser(String name, float value, int userHandle) {
+ return putStringForUser(name, Float.toString(value), userHandle);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
new file mode 100644
index 0000000..f36c335e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module for classes within com.android.systemui.util.settings.
+ */
+@Module
+public interface SettingsUtilModule {
+
+ /** Bind SecureSettingsImpl to SecureSettings. */
+ @Binds
+ SecureSettings bindsSecureSettings(SecureSettingsImpl impl);
+
+ /** Bind SystemSettingsImpl to SystemSettings. */
+ @Binds
+ SystemSettings bindsSystemSettings(SystemSettingsImpl impl);
+
+ /** Bind GlobalSettingsImpl to GlobalSettings. */
+ @Binds
+ GlobalSettings bindsGlobalSettings(GlobalSettingsImpl impl);
+}
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
similarity index 66%
copy from core/java/android/app/IRequestFinishCallback.aidl
copy to packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
index 3270565..d57d749 100644
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package android.app;
+package com.android.systemui.util.settings;
/**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
+ * Public interface that can be injected to interact with Settings.System.
*
- * {@hide}
+ * See {@link SettingsProxy} for details.
*/
-oneway interface IRequestFinishCallback {
- void requestFinish();
+public interface SystemSettings extends SettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
new file mode 100644
index 0000000..0dbb76f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SystemSettingsImpl implements SystemSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ SystemSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.System.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.System.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return Settings.System.putString(mContentResolver, name, value, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.System.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.Secure and Settings.Global");
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.Secure and Settings.Global");
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
index bba5ff5..fbc1676 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
@@ -52,16 +52,16 @@
@Singleton
@Provides
- static SystemWindows provideSystemWindows(Context context, DisplayController displayController,
+ static SystemWindows provideSystemWindows(DisplayController displayController,
IWindowManager wmService) {
- return new SystemWindows(context, displayController, wmService);
+ return new SystemWindows(displayController, wmService);
}
@Singleton
@Provides
static DisplayImeController provideDisplayImeController(
- SystemWindows syswin, DisplayController displayController,
+ IWindowManager wmService, DisplayController displayController,
@Main Handler mainHandler, TransactionPool transactionPool) {
- return new DisplayImeController(syswin, displayController, mainHandler, transactionPool);
+ return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 475ddc1..35be496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -46,9 +46,8 @@
@Test
public void testInitDependency() {
Dependency.clearDependencies();
- Dependency dependency = new Dependency();
- SystemUIFactory
- .getInstance().getRootComponent().createDependency().createSystemUI(dependency);
+ Dependency dependency =
+ SystemUIFactory.getInstance().getRootComponent().createDependency();
dependency.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index cf778504..3687b4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -54,7 +54,10 @@
@Before
public void SysuiSetup() {
SystemUIFactory.createFromConfig(mContext);
- mDependency = new TestableDependency(mContext);
+ mDependency = new TestableDependency(
+ SystemUIFactory.getInstance().getRootComponent().createDependency());
+ Dependency.setInstance(mDependency);
+
// TODO: Figure out another way to give reference to a SysuiTestableContext.
mSysuiContext = (SysuiTestableContext) mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index b6cc2ee..08e2784 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -73,7 +73,9 @@
@Before
public void SysuiSetup() throws Exception {
SystemUIFactory.createFromConfig(mContext);
- mDependency = new TestableDependency(mContext);
+ mDependency = new TestableDependency(
+ SystemUIFactory.getInstance().getRootComponent().createDependency());
+ Dependency.setInstance(mDependency);
mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
mock(Executor.class), mock(DumpManager.class),
mock(BroadcastDispatcherLogger.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index a7f4fa5..ee52c78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -16,7 +16,6 @@
import static org.mockito.Mockito.mock;
-import android.content.Context;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -26,13 +25,10 @@
private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>();
+ private final Dependency mParent;
- public TestableDependency(Context context) {
- SystemUIFactory.createFromConfig(context);
- SystemUIFactory.getInstance().getRootComponent()
- .createDependency()
- .createSystemUI(this);
- start();
+ public TestableDependency(Dependency parent) {
+ mParent = parent;
}
public <T> T injectMockDependency(Class<T> cls) {
@@ -53,11 +49,11 @@
}
@Override
- protected <T> T createDependency(Object key) {
+ public <T> T createDependency(Object key) {
if (mObjs.containsKey(key)) return (T) mObjs.get(key);
mInstantiatedObjects.add(key);
- return super.createDependency(key);
+ return mParent.createDependency(key);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
new file mode 100644
index 0000000..9cb4fb3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.display.DisplayManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DisplayIdIndexSupplierTest extends SysuiTestCase {
+
+ private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
+
+ @Before
+ public void setUp() throws Exception {
+ mDisplayIdIndexSupplier = new DisplayIdIndexSupplier(
+ mContext.getSystemService(DisplayManager.class)) {
+
+ @NonNull
+ @Override
+ protected Object createInstance(Display display) {
+ return new Object();
+ }
+ };
+ }
+
+ @Test
+ public void get_instanceIsNotNull() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ assertNotNull(object);
+ }
+
+ @Test
+ public void get_removeExistedObject_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.remove(Display.DEFAULT_DISPLAY);
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+
+ @Test
+ public void get_clearAllObjects_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.clear();
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index d6d2fcd..6948279 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -16,20 +16,13 @@
package com.android.systemui.accessibility;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
import androidx.test.filters.SmallTest;
@@ -38,45 +31,43 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
/** Tests the ModeSwitchesController. */
public class ModeSwitchesControllerTest extends SysuiTestCase {
- private WindowManager mWindowManager;
+ @Mock
+ private ModeSwitchesController.SwitchSupplier mSupplier;
+ @Mock
+ private MagnificationModeSwitch mModeSwitch;
private ModeSwitchesController mModeSwitchesController;
+
@Before
public void setUp() {
- mWindowManager = mock(WindowManager.class);
- Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
- when(mWindowManager.getDefaultDisplay()).thenReturn(display);
- WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
- .getMaximumWindowMetrics();
- when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- mModeSwitchesController = new ModeSwitchesController(mContext);
+ MockitoAnnotations.initMocks(this);
+ when(mSupplier.get(anyInt())).thenReturn(mModeSwitch);
+ mModeSwitchesController = new ModeSwitchesController(mSupplier);
}
@Test
public void testShowButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- verify(mWindowManager).addView(any(), any());
+
+ verify(mModeSwitch).showButton(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
@Test
public void testRemoveButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
- verify(mWindowManager).addView(captor.capture(), any(WindowManager.LayoutParams.class));
mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);
- verify(mWindowManager).removeView(eq(captor.getValue()));
+ verify(mModeSwitch).removeButton();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index e0049d1..4fdc06e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -26,11 +26,14 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
import android.os.Looper;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
@@ -56,6 +59,7 @@
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
+ private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@Mock
private AppOpsManager mAppOpsManager;
@@ -65,6 +69,10 @@
private AppOpsControllerImpl.H mMockHandler;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private PermissionFlagsCache mFlagsCache;
+ @Mock
+ private PackageManager mPackageManager;
private AppOpsControllerImpl mController;
private TestableLooper mTestableLooper;
@@ -76,8 +84,22 @@
getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
- mController =
- new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mDumpManager);
+ // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
+ // TEST_UID_NON_USER_SENSITIVE are user sensitive.
+ getContext().setMockPackageManager(mPackageManager);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID))).thenReturn(
+ PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_OTHER)))
+ .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
+ eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
+
+ mController = new AppOpsControllerImpl(
+ mContext,
+ mTestableLooper.getLooper(),
+ mDumpManager,
+ mFlagsCache
+ );
}
@Test
@@ -173,6 +195,26 @@
}
@Test
+ public void nonUserSensitiveOpsAreIgnored() {
+ mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+ assertEquals(0, mController.getActiveAppOpsForUser(
+ UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+ }
+
+ @Test
+ public void nonUserSensitiveOpsNotNotified() {
+ mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never())
+ .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean());
+ }
+
+ @Test
public void opNotedScheduledForRemoval() {
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
new file mode 100644
index 0000000..0fb0ce0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PermissionFlagsCacheTest : SysuiTestCase() {
+
+ companion object {
+ const val TEST_PERMISSION = "test_permission"
+ const val TEST_PACKAGE = "test_package"
+ const val TEST_UID1 = 1000
+ const val TEST_UID2 = UserHandle.PER_USER_RANGE + 1000
+ }
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+
+ private lateinit var executor: FakeExecutor
+ private lateinit var flagsCache: PermissionFlagsCache
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ executor = FakeExecutor(FakeSystemClock())
+
+ flagsCache = PermissionFlagsCache(packageManager, executor)
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testNotListeningByDefault() {
+ verify(packageManager, never()).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testGetCorrectFlags() {
+ `when`(packageManager.getPermissionFlags(anyString(), anyString(), any())).thenReturn(0)
+ `when`(packageManager.getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1))
+ ).thenReturn(1)
+
+ assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+ assertNotEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2))
+ }
+
+ @Test
+ fun testFlagIsCached() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ verify(packageManager, times(1)).getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1)
+ )
+ }
+
+ @Test
+ fun testListeningAfterFirstRequest() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ verify(packageManager).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testListeningOnlyOnce() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2)
+
+ verify(packageManager, times(1)).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testUpdateFlag() {
+ assertEquals(0, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+
+ `when`(packageManager.getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1))
+ ).thenReturn(1)
+
+ flagsCache.onPermissionsChanged(TEST_UID1)
+
+ executor.runAllReady()
+
+ assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+ }
+
+ @Test
+ fun testUpdateFlag_notUpdatedIfUidHasNotBeenRequestedBefore() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.onPermissionsChanged(TEST_UID2)
+
+ executor.runAllReady()
+
+ verify(packageManager, never()).getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID2)
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e8a0c73..d4a94c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -45,6 +45,7 @@
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -530,6 +531,11 @@
IActivityTaskManager getActivityTaskManager() {
return mock(IActivityTaskManager.class);
}
+
+ @Override
+ FingerprintManager getFingerprintManager(Context context) {
+ return mock(FingerprintManager.class);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 7ebead8..4f0ff42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before;
@@ -84,6 +85,7 @@
private DozeLog mDozeLog;
@Mock
private ProximitySensor mProximitySensor;
+ private FakeSettings mFakeSettings = new FakeSettings();
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper;
private DozeSensors mDozeSensors;
@@ -154,7 +156,7 @@
TestableDozeSensors() {
super(getContext(), mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
- mProximitySensor);
+ mProximitySensor, mFakeSettings);
for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index d3af835..1ed5871 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.util.sensors.FakeSensorManager;
import com.android.systemui.util.sensors.FakeThresholdSensor;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -99,7 +100,7 @@
mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
- mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher);
+ mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings());
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
index 7a234a4..ffedb07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,6 +43,9 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedUITest extends OneHandedTestCase {
+ private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+
+ boolean mIsSupportOneHandedMode;
CommandQueue mCommandQueue;
KeyguardUpdateMonitor mKeyguardUpdateMonitor;
OneHandedUI mOneHandedUI;
@@ -58,6 +62,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mIsSupportOneHandedMode = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
mCommandQueue = new CommandQueue(mContext);
mScreenLifecycle = new ScreenLifecycle();
mOneHandedUI = new OneHandedUI(mContext,
@@ -72,6 +77,10 @@
@Test
public void testStartOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mOneHandedUI.startOneHanded();
verify(mMockOneHandedManagerImpl, times(1)).startOneHanded();
@@ -79,6 +88,10 @@
@Test
public void testStopOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mOneHandedUI.stopOneHanded();
verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -86,6 +99,10 @@
@Test
public void testRegisterSettingsObserver_forEnabled() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
final String key = Settings.Secure.ONE_HANDED_MODE_ENABLED;
verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -93,6 +110,10 @@
@Test
public void testRegisterSettingsObserver_forTimeout() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
final String key = Settings.Secure.ONE_HANDED_MODE_TIMEOUT;
verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -100,6 +121,10 @@
@Test
public void testRegisterSettingsObserver_forTapAppExit() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
final String key = Settings.Secure.TAPS_APP_TO_EXIT;
verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -107,6 +132,10 @@
@Test
public void tesSettingsObserver_updateTapAppToExit() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.TAPS_APP_TO_EXIT, 1);
@@ -115,6 +144,10 @@
@Test
public void tesSettingsObserver_updateEnabled() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
@@ -123,6 +156,10 @@
@Test
public void tesSettingsObserver_updateTimeout() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
@@ -134,6 +171,10 @@
@Ignore("Clarifying do not receive callback")
@Test
public void testKeyguardBouncerShowing_shouldStopOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -141,6 +182,10 @@
@Test
public void testScreenTurningOff_shouldStopOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mScreenLifecycle.dispatchScreenTurningOff();
verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
new file mode 100644
index 0000000..dcee5a716
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PrivacyChipBuilderTest : SysuiTestCase() {
+
+ companion object {
+ val TEST_UID = 1
+ }
+
+ @Test
+ fun testGenerateAppsList() {
+ val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+ "Bar", TEST_UID))
+ val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
+ "Bar", TEST_UID))
+ val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication(
+ "Foo", TEST_UID))
+ val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+ "Baz", TEST_UID))
+
+ val items = listOf(bar2, foo0, baz1, bar3)
+
+ val textBuilder = PrivacyChipBuilder(context, items)
+
+ val list = textBuilder.appsAndTypes
+ assertEquals(3, list.size)
+ val appsList = list.map { it.first }
+ val typesList = list.map { it.second }
+ // List is sorted by number of types and then by types
+ assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
+ assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
+ assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
+ assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2])
+ }
+
+ @Test
+ fun testOrder() {
+ // We want location to always go last, so it will go in the "+ other apps"
+ val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication("Camera", TEST_UID))
+ val appMicrophone =
+ PrivacyItem(PrivacyType.TYPE_MICROPHONE,
+ PrivacyApplication("Microphone", TEST_UID))
+ val appLocation =
+ PrivacyItem(PrivacyType.TYPE_LOCATION,
+ PrivacyApplication("Location", TEST_UID))
+
+ val items = listOf(appLocation, appMicrophone, appCamera)
+ val textBuilder = PrivacyChipBuilder(context, items)
+ val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
+ assertEquals(listOf("Camera", "Microphone", "Location"), appList)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
new file mode 100644
index 0000000..dddc350
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.hamcrest.Matchers.hasItem
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyList
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class PrivacyItemControllerTest : SysuiTestCase() {
+
+ companion object {
+ val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+ val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
+ const val SYSTEM_UID = 1000
+ const val TEST_PACKAGE_NAME = "test"
+ const val DEVICE_SERVICES_STRING = "Device services"
+ const val TAG = "PrivacyItemControllerTest"
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ fun <T> any(): T = Mockito.any<T>()
+ }
+
+ @Mock
+ private lateinit var appOpsController: AppOpsController
+ @Mock
+ private lateinit var callback: PrivacyItemController.Callback
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var dumpManager: DumpManager
+ @Captor
+ private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
+ @Captor
+ private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
+
+ private lateinit var privacyItemController: PrivacyItemController
+ private lateinit var executor: FakeExecutor
+ private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+ fun PrivacyItemController(context: Context): PrivacyItemController {
+ return PrivacyItemController(
+ context,
+ appOpsController,
+ executor,
+ executor,
+ broadcastDispatcher,
+ deviceConfigProxy,
+ userManager,
+ dumpManager
+ )
+ }
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ executor = FakeExecutor(FakeSystemClock())
+ deviceConfigProxy = DeviceConfigProxyFake()
+
+ appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
+
+ deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+ "true", false)
+
+ doReturn(listOf(object : UserInfo() {
+ init {
+ id = CURRENT_USER_ID
+ }
+ })).`when`(userManager).getProfiles(anyInt())
+
+ privacyItemController = PrivacyItemController(mContext)
+ }
+
+ @Test
+ fun testSetListeningTrueByAddingCallback() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
+ any())
+ verify(callback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testSetListeningFalseByRemovingLastCallback() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).removeCallback(any(),
+ any())
+ privacyItemController.removeCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
+ any())
+ verify(callback).onPrivacyItemsChanged(emptyList())
+ }
+
+ @Test
+ fun testDistinctItems() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ }
+
+ @Test
+ fun testRegisterReceiver_allUsers() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(broadcastDispatcher, atLeastOnce()).registerReceiver(
+ eq(privacyItemController.userSwitcherReceiver), any(), eq(null), eq(UserHandle.ALL))
+ verify(broadcastDispatcher, never())
+ .unregisterReceiver(eq(privacyItemController.userSwitcherReceiver))
+ }
+
+ @Test
+ fun testReceiver_ACTION_USER_FOREGROUND() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_USER_SWITCHED))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testAddMultipleCallbacks() {
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(anyList())
+
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ // Adding a callback should not unnecessarily call previous ones
+ verifyNoMoreInteractions(callback)
+ }
+
+ @Test
+ fun testMultipleCallbacksAreUpdated() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(anyList())
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testRemoveCallback() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ executor.runAllReady()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ privacyItemController.removeCallback(callback)
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ executor.runAllReady()
+ verify(callback, never()).onPrivacyItemsChanged(anyList())
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testListShouldNotHaveNull() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ executor.runAllReady()
+
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ assertThat(argCaptor.value, not(hasItem(nullValue())))
+ }
+
+ @Test
+ fun testListShouldBeCopy() {
+ val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication("", TEST_UID)))
+ privacyItemController.privacyList = list
+ val privacyList = privacyItemController.privacyList
+ assertEquals(list, privacyList)
+ assertTrue(list !== privacyList)
+ }
+
+ @Test
+ fun testNotListeningWhenIndicatorsDisabled() {
+ deviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+ "false",
+ false
+ )
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
+ any())
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
new file mode 100644
index 0000000..4aaafbd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class ActionProxyReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private StatusBar mMockStatusBar;
+ @Mock
+ private ActivityManagerWrapper mMockActivityManagerWrapper;
+ @Mock
+ private Future mMockFuture;
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private PendingIntent mMockPendingIntent;
+
+ private Intent mIntent;
+
+ @Before
+ public void setup() throws InterruptedException, ExecutionException, TimeoutException {
+ MockitoAnnotations.initMocks(this);
+ mIntent = new Intent(mContext, ActionProxyReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+
+ when(mMockActivityManagerWrapper.closeSystemWindows(anyString())).thenReturn(mMockFuture);
+ when(mMockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null);
+ }
+
+ @Test
+ public void testPendingIntentSentWithoutStatusBar() throws PendingIntent.CanceledException {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(false);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+ verify(mMockStatusBar, never()).executeRunnableDismissingKeyguard(
+ any(Runnable.class), any(Runnable.class), anyBoolean(), anyBoolean(), anyBoolean());
+ verify(mMockPendingIntent).send(
+ eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ }
+
+ @Test
+ public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+ // ensure that the pending intent call is passed through
+ doAnswer((Answer<Object>) invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mMockStatusBar).executeRunnableDismissingKeyguard(
+ any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+ verify(mMockStatusBar).executeRunnableDismissingKeyguard(
+ any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
+ verify(mMockPendingIntent).send(
+ eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ }
+
+ @Test
+ public void testSmartActionsNotNotifiedByDefault() {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockScreenshotSmartActions, never())
+ .notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean());
+ }
+
+ @Test
+ public void testSmartActionsNotifiedIfEnabled() {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+ mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+ String testId = "testID";
+ mIntent.putExtra(EXTRA_ID, testId);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, ACTION_TYPE_SHARE, false);
+ }
+
+ private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
+ if (withStatusBar) {
+ return new ActionProxyReceiver(
+ Optional.of(mMockStatusBar), mMockActivityManagerWrapper,
+ mMockScreenshotSmartActions);
+ } else {
+ return new ActionProxyReceiver(
+ Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
new file mode 100644
index 0000000..b924913
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class DeleteScreenshotReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private Executor mMockExecutor;
+
+ private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mDeleteScreenshotReceiver =
+ new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
+ }
+
+ @Test
+ public void testNoUriProvided() {
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+
+ mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+ verify(mMockExecutor, never()).execute(any(Runnable.class));
+ verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+ any(Context.class), any(String.class), any(String.class), anyBoolean());
+ }
+
+ @Test
+ public void testFileDeleted() {
+ DeleteScreenshotReceiver deleteScreenshotReceiver =
+ new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
+ ContentResolver contentResolver = mContext.getContentResolver();
+ final Uri testUri = contentResolver.insert(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
+ assertNotNull(testUri);
+
+ try {
+ Cursor cursor =
+ contentResolver.query(testUri, null, null, null, null);
+ assertEquals(1, cursor.getCount());
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
+ .putExtra(SCREENSHOT_URI_ID, testUri.toString());
+
+ deleteScreenshotReceiver.onReceive(mContext, intent);
+ int runCount = mFakeExecutor.runAllReady();
+
+ assertEquals(1, runCount);
+ cursor =
+ contentResolver.query(testUri, null, null, null, null);
+ assertEquals(0, cursor.getCount());
+ } finally {
+ contentResolver.delete(testUri, null, null);
+ }
+
+ // ensure smart actions not called by default
+ verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+ any(Context.class), any(String.class), any(String.class), anyBoolean());
+ }
+
+ @Test
+ public void testNotifyScreenshotAction() {
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+ String uriString = "testUri";
+ String testId = "testID";
+ intent.putExtra(SCREENSHOT_URI_ID, uriString);
+ intent.putExtra(EXTRA_ID, testId);
+ intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+
+ mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+ verify(mMockExecutor).execute(any(Runnable.class));
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, ACTION_TYPE_DELETE, false);
+ }
+
+ private static ContentValues getFakeContentValues() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
+ + File.separator + Environment.DIRECTORY_SCREENSHOTS);
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
+ values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
+ values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
+ values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
+ return values;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index d3b3399..184329ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -61,12 +61,14 @@
*/
public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+ private ScreenshotSmartActions mScreenshotSmartActions;
private Handler mHandler;
@Before
public void setup() {
mSmartActionsProvider = mock(
ScreenshotNotificationSmartActionsProvider.class);
+ mScreenshotSmartActions = new ScreenshotSmartActions();
mHandler = mock(Handler.class);
}
@@ -82,7 +84,7 @@
when(smartActionsProvider.getActions(any(), any(), any(), any(), any()))
.thenThrow(RuntimeException.class);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
assertNotNull(smartActionsFuture);
@@ -100,7 +102,7 @@
int timeoutMs = 1000;
when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
RuntimeException.class);
- List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions(
+ List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions(
"", smartActionsFuture, timeoutMs, mSmartActionsProvider);
assertEquals(Collections.emptyList(), actions);
}
@@ -111,7 +113,7 @@
throws Exception {
doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
anyLong());
- ScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
+ mScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
}
// Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
@@ -122,7 +124,7 @@
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any());
@@ -136,7 +138,7 @@
public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
UserHandle.getUserHandleForUid(UserHandle.myUserId()));
verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any());
@@ -152,7 +154,7 @@
SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
mContext, null, mHandler);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
+ mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
actionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
assertNotNull(smartActionsFuture);
@@ -172,7 +174,8 @@
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png"));
@@ -198,7 +201,8 @@
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png"));
@@ -224,7 +228,8 @@
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action deleteAction = task.createDeleteAction(mContext,
mContext.getResources(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
new file mode 100644
index 0000000..ce6f073
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+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;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SmartActionsReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private PendingIntent mMockPendingIntent;
+
+ private SmartActionsReceiver mSmartActionsReceiver;
+ private Intent mIntent;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
+ mIntent = new Intent(mContext, SmartActionsReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+ }
+
+ @Test
+ public void testSmartActionIntent() throws PendingIntent.CanceledException {
+ String testId = "testID";
+ String testActionType = "testActionType";
+ mIntent.putExtra(EXTRA_ID, testId);
+ mIntent.putExtra(EXTRA_ACTION_TYPE, testActionType);
+
+ mSmartActionsReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockPendingIntent).send(
+ eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, testActionType, true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index aefea57..8a49326 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -61,6 +61,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,9 @@
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index c5374b2..601df2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -184,7 +184,9 @@
() -> mRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 243503d..7ca2478 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -380,7 +380,7 @@
setupMockStack(
PEOPLE_HEADER,
- ALERTING.headsUp(),
+ ALERTING,
PERSON,
ALERTING_HEADER,
GENTLE_HEADER,
@@ -403,9 +403,9 @@
enablePeopleFiltering();
setupMockStack(
- PERSON.headsUp(),
+ PERSON,
INCOMING_HEADER,
- ALERTING.headsUp(),
+ ALERTING,
PEOPLE_HEADER,
PERSON
);
@@ -425,7 +425,7 @@
enablePeopleFiltering();
setupMockStack(
- PERSON.headsUp(),
+ PERSON,
PEOPLE_HEADER,
PERSON
);
@@ -443,8 +443,8 @@
enablePeopleFiltering();
setupMockStack(
- ALERTING.headsUp(),
- PERSON.headsUp()
+ ALERTING,
+ PERSON
);
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
@@ -461,7 +461,7 @@
setupMockStack(
INCOMING_HEADER,
- ALERTING.headsUp(),
+ ALERTING,
PEOPLE_HEADER,
FSN,
PERSON,
@@ -502,9 +502,9 @@
public void testMediaControls_AddWhenEnterKeyguardWithHeadsUp() {
enableMediaControls();
- // GIVEN a stack that doesn't include media controls but includes HEADS_UP
+ // GIVEN a stack that doesn't include media
setupMockStack(
- ALERTING.headsUp(),
+ ALERTING,
ALERTING,
GENTLE_HEADER,
GENTLE);
@@ -584,6 +584,27 @@
);
}
+ @Test
+ public void testIgnoreGoneView() {
+ enablePeopleFiltering();
+
+ setupMockStack(
+ PERSON.gone(),
+ ALERTING,
+ GENTLE
+ );
+
+ mSectionsManager.updateSectionBoundaries();
+
+ verifyMockStack(
+ ChildType.ALERTING_HEADER,
+ ChildType.PERSON,
+ ChildType.ALERTING,
+ ChildType.GENTLE_HEADER,
+ ChildType.GENTLE
+ );
+ }
+
private void enablePeopleFiltering() {
when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
}
@@ -619,16 +640,16 @@
child = mSectionsManager.getSilentHeaderView();
break;
case FSN:
- child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
break;
case PERSON:
- child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
break;
case ALERTING:
- child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
break;
case GENTLE:
- child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_SILENT, entry.mIsGone);
break;
case OTHER:
child = mock(View.class);
@@ -643,7 +664,7 @@
}
}
- private View mockNotification(int bucket, boolean headsUp) {
+ private View mockNotification(int bucket, boolean isGone) {
ExpandableNotificationRow notifRow =
mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
@@ -659,8 +680,7 @@
return null;
}).when(mockEntry).setBucket(anyInt());
- when(notifRow.isHeadsUp()).thenReturn(headsUp);
- when(mockEntry.isRowHeadsUp()).thenReturn(headsUp);
+ when(notifRow.getVisibility()).thenReturn(isGone ? View.GONE : View.VISIBLE);
return notifRow;
}
@@ -767,16 +787,16 @@
child = mSectionsManager.getSilentHeaderView();
break;
case FSN:
- child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
break;
case PERSON:
- child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
break;
case ALERTING:
- child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
break;
case GENTLE:
- child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_SILENT, entry.mIsGone);
break;
case OTHER:
child = mock(View.class);
@@ -796,36 +816,25 @@
private static final StackEntry ALERTING_HEADER = new StackEntry(ChildType.ALERTING_HEADER);
private static final StackEntry GENTLE_HEADER = new StackEntry(ChildType.GENTLE_HEADER);
private static final StackEntry FSN = new StackEntry(ChildType.FSN);
- private static final StackEntry.Hunnable PERSON = new StackEntry.Hunnable(ChildType.PERSON);
- private static final StackEntry.Hunnable ALERTING = new StackEntry.Hunnable(ChildType.ALERTING);
+ private static final StackEntry PERSON = new StackEntry(ChildType.PERSON);
+ private static final StackEntry ALERTING = new StackEntry(ChildType.ALERTING);
private static final StackEntry GENTLE = new StackEntry(ChildType.GENTLE);
private static class StackEntry {
final ChildType mChildType;
- final boolean mIsHeadsUp;
+ final boolean mIsGone;
StackEntry(ChildType childType) {
this(childType, false);
}
- StackEntry(ChildType childType, boolean isHeadsUp) {
+ StackEntry(ChildType childType, boolean isGone) {
mChildType = childType;
- mIsHeadsUp = isHeadsUp;
+ mIsGone = isGone;
}
- static class Hunnable extends StackEntry {
-
- Hunnable(ChildType childType) {
- super(childType, false);
- }
-
- Hunnable(ChildType childType, boolean isHeadsUp) {
- super(childType, isHeadsUp);
- }
-
- public Hunnable headsUp() {
- return new Hunnable(mChildType, true);
- }
+ public StackEntry gone() {
+ return new StackEntry(mChildType, true);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b286f94..6d411333 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -55,6 +55,7 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -92,6 +93,7 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.leak.LeakDetector;
@@ -190,7 +192,9 @@
() -> mock(NotificationRowBinder.class),
() -> mRemoteInputManager,
mock(LeakDetector.class),
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class));
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index debc840..fa253e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -16,13 +16,18 @@
package com.android.systemui.statusbar.phone;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
+import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -30,6 +35,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
import org.junit.Assert;
@@ -50,6 +56,7 @@
@Mock private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
@Mock private PowerManager mPowerManager;
@Mock private TunerService mTunerService;
+ @Mock private BatteryController mBatteryController;
@Before
public void setup() {
@@ -59,11 +66,12 @@
mAmbientDisplayConfiguration,
mAlwaysOnDisplayPolicy,
mPowerManager,
+ mBatteryController,
mTunerService
);
}
@Test
- public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
mDozeParameters.setControlScreenOffAnimation(true);
reset(mPowerManager);
mDozeParameters.setControlScreenOffAnimation(false);
@@ -71,7 +79,7 @@
}
@Test
- public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
mDozeParameters.setControlScreenOffAnimation(false);
reset(mPowerManager);
mDozeParameters.setControlScreenOffAnimation(true);
@@ -79,11 +87,28 @@
}
@Test
- public void test_getWallpaperAodDuration_when_shouldControlScreenOff() {
+ public void testGetWallpaperAodDuration_when_shouldControlScreenOff() {
mDozeParameters.setControlScreenOffAnimation(true);
Assert.assertEquals(
"wallpaper hides faster when controlling screen off",
mDozeParameters.getWallpaperAodDuration(),
DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY);
}
+
+ @Test
+ public void testGetAlwaysOn() {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+ assertThat(mDozeParameters.getAlwaysOn()).isTrue();
+ }
+
+ @Test
+ public void testGetAlwaysOn_whenBatterySaver() {
+ when(mBatteryController.isAodPowerSave()).thenReturn(true);
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+ assertThat(mDozeParameters.getAlwaysOn()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 4d6922c..0c2361a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -24,6 +24,7 @@
import android.app.AppOpsManager;
import android.content.Intent;
import android.location.LocationManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -60,8 +61,11 @@
mLocationController = spy(new LocationControllerImpl(mContext,
mAppOpsController,
mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
mock(BroadcastDispatcher.class),
mock(BootCompleteCache.class)));
+
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
new file mode 100644
index 0000000..8cb5f3e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FakeSettings implements SecureSettings, GlobalSettings, SystemSettings {
+ private final Map<SettingsKey, String> mValues = new HashMap<>();
+ private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
+ new HashMap<>();
+
+ public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
+
+ public FakeSettings() {
+ }
+
+ public FakeSettings(String initialKey, String initialValue) {
+ putString(initialKey, initialValue);
+ }
+
+ public FakeSettings(Map<String, String> initialValues) {
+ for (Map.Entry<String, String> kv : initialValues.entrySet()) {
+ putString(kv.getKey(), kv.getValue());
+ }
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return null;
+ }
+
+ @Override
+ public void registerContentObserverForUser(String name, ContentObserver settingsObserver,
+ int userHandle) {
+ SettingsKey key = new SettingsKey(userHandle, name);
+ mContentObservers.putIfAbsent(key, new ArrayList<>());
+ List<ContentObserver> observers = mContentObservers.get(key);
+ observers.add(settingsObserver);
+ }
+
+ @Override
+ public void unregisterContentObserver(ContentObserver settingsObserver) {
+ for (SettingsKey key : mContentObservers.keySet()) {
+ List<ContentObserver> observers = mContentObservers.get(key);
+ observers.remove(settingsObserver);
+ }
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Uri.withAppendedPath(CONTENT_URI, name);
+ }
+
+ @Override
+ public int getUserId() {
+ return UserHandle.USER_CURRENT;
+ }
+
+ @Override
+ public String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return mValues.get(new SettingsKey(userHandle, name));
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return putStringForUser(name, value, null, false, getUserId(), overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value) {
+ return putString(name, value, false);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return putStringForUser(name, value, null, false, userHandle, false);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ SettingsKey key = new SettingsKey(userHandle, name);
+ mValues.put(key, value);
+
+ Uri uri = getUriFor(name);
+ for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
+ observer.dispatchChange(false, List.of(uri), userHandle);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return putString(name, value);
+ }
+
+ private static class SettingsKey extends Pair<Integer, String> {
+ SettingsKey(Integer first, String second) {
+ super(first, second);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
new file mode 100644
index 0000000..0d560f2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+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;
+
+import java.util.Collection;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class FakeSettingsTest extends SysuiTestCase {
+ @Mock
+ ContentObserver mContentObserver;
+
+ private FakeSettings mFakeSettings;
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mFakeSettings = new FakeSettings();
+ }
+
+ /**
+ * Test FakeExecutor that receives non-delayed items to execute.
+ */
+ @Test
+ public void testPutAndGet() throws Settings.SettingNotFoundException {
+ mFakeSettings.putInt("foobar", 1);
+ assertThat(mFakeSettings.getInt("foobar")).isEqualTo(1);
+ }
+
+ @Test
+ public void testInitialize() {
+ mFakeSettings = new FakeSettings("abra", "cadabra");
+ assertThat(mFakeSettings.getString("abra")).isEqualTo("cadabra");
+ }
+
+ @Test
+ public void testInitializeWithMap() {
+ mFakeSettings = new FakeSettings(Map.of("one fish", "two fish", "red fish", "blue fish"));
+ assertThat(mFakeSettings.getString("red fish")).isEqualTo("blue fish");
+ assertThat(mFakeSettings.getString("one fish")).isEqualTo("two fish");
+ }
+
+ @Test
+ public void testRegisterContentObserver() {
+ mFakeSettings.registerContentObserver("cat", mContentObserver);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
+ }
+
+ @Test
+ public void testUnregisterContentObserver() {
+ mFakeSettings.registerContentObserver("cat", mContentObserver);
+ mFakeSettings.unregisterContentObserver(mContentObserver);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver, never()).dispatchChange(
+ anyBoolean(), any(Collection.class), anyInt());
+ }
+}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 7f6dc14..c87dcd7 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -75,6 +75,16 @@
*/
@VisibleForTesting static final long POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS = 500;
+ /**
+ * Number of taps required to launch panic ui.
+ */
+ private static final int PANIC_POWER_TAP_COUNT_THRESHOLD = 5;
+
+ /**
+ * Number of taps required to launch camera shortcut.
+ */
+ private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
private final CameraLiftTriggerEventListener mCameraLiftTriggerListener =
@@ -135,6 +145,7 @@
private long mLastPowerDown;
private int mPowerButtonConsecutiveTaps;
+ private int mPowerButtonSlowConsecutiveTaps;
public GestureLauncherService(Context context) {
this(context, new MetricsLogger());
@@ -350,9 +361,8 @@
* Whether to enable panic button gesture.
*/
public static boolean isPanicButtonGestureEnabled(Context context, int userId) {
- return isCameraLaunchEnabled(context.getResources())
- && (Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0);
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0;
}
/**
@@ -384,6 +394,13 @@
isCameraLiftTriggerEnabled(resources);
}
+ /**
+ * Attempts to intercept power key down event by detecting certain gesture patterns
+ *
+ * @param interactive true if the event's policy contains {@code FLAG_INTERACTIVE}
+ * @param outLaunched true if some action is taken as part of the key intercept (eg, app launch)
+ * @return true if the key down event is intercepted
+ */
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
if (event.isLongPress()) {
@@ -392,41 +409,60 @@
// taps or consecutive taps, so we want to ignore the long press event.
return false;
}
- boolean launched = false;
+ boolean launchCamera = false;
+ boolean launchPanic = false;
boolean intercept = false;
long powerTapInterval;
synchronized (this) {
powerTapInterval = event.getEventTime() - mLastPowerDown;
- if (mCameraDoubleTapPowerEnabled
- && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
- launched = true;
- intercept = interactive;
- mPowerButtonConsecutiveTaps++;
- } else if (powerTapInterval < POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
- mPowerButtonConsecutiveTaps++;
- } else {
- mPowerButtonConsecutiveTaps = 1;
- }
mLastPowerDown = event.getEventTime();
+ if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
+ // Tap too slow, reset consecutive tap counts.
+ mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps = 1;
+ } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+ // Tap too slow for shortcuts
+ mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps++;
+ } else {
+ // Fast consecutive tap
+ mPowerButtonConsecutiveTaps++;
+ mPowerButtonSlowConsecutiveTaps++;
+ }
+ if (mPanicButtonGestureEnabled
+ && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
+ launchPanic = true;
+ intercept = interactive;
+ } else if (mCameraDoubleTapPowerEnabled
+ && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+ && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+ launchCamera = true;
+ intercept = interactive;
+ }
}
- if (DBG && mPowerButtonConsecutiveTaps > 1) {
- Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps) +
- " consecutive power button taps detected");
+ if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
+ Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps)
+ + " consecutive power button taps detected, "
+ + Long.valueOf(mPowerButtonSlowConsecutiveTaps)
+ + " consecutive slow power button taps detected");
}
- if (launched) {
+ if (launchCamera) {
Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
+ powerTapInterval + "ms");
- launched = handleCameraGesture(false /* useWakelock */,
+ launchCamera = handleCameraGesture(false /* useWakelock */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
- if (launched) {
+ if (launchCamera) {
mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
(int) powerTapInterval);
}
+ } else if (launchPanic) {
+ Slog.i(TAG, "Panic gesture detected, launching panic.");
}
- mMetricsLogger.histogram("power_consecutive_short_tap_count", mPowerButtonConsecutiveTaps);
+ mMetricsLogger.histogram("power_consecutive_short_tap_count",
+ mPowerButtonSlowConsecutiveTaps);
mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
- outLaunched.value = launched;
- return intercept && launched;
+ outLaunched.value = launchCamera || launchPanic;
+ return intercept && (launchCamera || launchPanic);
}
/**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 816d663..1520dd3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3238,6 +3238,12 @@
@Override
public void lockUserKey(int userId) {
+ // Do not lock user 0 data for headless system user
+ if (userId == UserHandle.USER_SYSTEM
+ && UserManager.isHeadlessSystemUserMode()) {
+ throw new IllegalArgumentException("Headless system user data cannot be locked..");
+ }
+
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 676b767..a4c6c87 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -63,7 +63,6 @@
import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
-import android.telephony.DataFailCause;
import android.telephony.DisconnectCause;
import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneCapability;
@@ -1800,11 +1799,9 @@
if (validatePhoneId(phoneId)) {
mPreciseDataConnectionStates.get(phoneId).put(
apnType,
- new PreciseDataConnectionState(
- TelephonyManager.DATA_UNKNOWN,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- apnType, null, null,
- DataFailCause.NONE, null));
+ new PreciseDataConnectionState.Builder()
+ .setApnTypes(apnType)
+ .build());
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -1986,11 +1983,10 @@
if (validatePhoneId(phoneId)) {
mPreciseDataConnectionStates.get(phoneId).put(
apnType,
- new PreciseDataConnectionState(
- TelephonyManager.DATA_UNKNOWN,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- apnType, null, null,
- failCause, null));
+ new PreciseDataConnectionState.Builder()
+ .setApnTypes(apnType)
+ .setFailCause(failCause)
+ .build());
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index be080e5..915189c 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -81,6 +81,7 @@
import java.util.Map;
import java.util.Set;
+import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
import static android.app.UiModeManager.MODE_NIGHT_YES;
@@ -1446,6 +1447,8 @@
pw.println(" Print this help text.");
pw.println(" night [yes|no|auto|custom]");
pw.println(" Set or read night mode.");
+ pw.println(" car [yes|no]");
+ pw.println(" Set or read car mode.");
pw.println(" time [start|end] <ISO time>");
pw.println(" Set custom start/end schedule time"
+ " (night mode must be set to custom to apply).");
@@ -1461,6 +1464,8 @@
switch (cmd) {
case "night":
return handleNightMode();
+ case "car":
+ return handleCarMode();
case "time":
return handleCustomTime();
default:
@@ -1558,6 +1563,34 @@
return -1;
}
}
+
+ private int handleCarMode() throws RemoteException {
+ final PrintWriter err = getErrPrintWriter();
+ final String modeStr = getNextArg();
+ if (modeStr == null) {
+ printCurrentCarMode();
+ return 0;
+ }
+
+ if (modeStr.equals("yes")) {
+ mInterface.enableCarMode(0 /* flags */, DEFAULT_PRIORITY, "" /* package */);
+ printCurrentCarMode();
+ return 0;
+ } else if (modeStr.equals("no")) {
+ mInterface.disableCarMode(0 /* flags */);
+ printCurrentCarMode();
+ return 0;
+ } else {
+ err.println("Error: mode must be 'yes', or 'no'");
+ return -1;
+ }
+ }
+
+ private void printCurrentCarMode() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int currMode = mInterface.getCurrentModeType();
+ pw.println("Car mode: " + (currMode == Configuration.UI_MODE_TYPE_CAR ? "yes" : "no"));
+ }
}
public final class LocalService extends UiModeManagerInternal {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 32f7d1d..555bf7d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -291,6 +291,7 @@
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -310,6 +311,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.ProcessMap;
@@ -657,6 +659,14 @@
int mUidChangeDispatchCount;
/**
+ * Uids of apps with current active camera sessions. Access synchronized on
+ * the IntArray instance itself, and no other locks must be acquired while that
+ * one is held.
+ */
+ @GuardedBy("mActiveCameraUids")
+ final IntArray mActiveCameraUids = new IntArray(4);
+
+ /**
* Helper class which strips out priority and proto arguments then calls the dump function with
* the appropriate arguments. If priority arguments are omitted, function calls the legacy
* dump command.
@@ -2053,7 +2063,10 @@
}
if (proc != null) {
long startTime = SystemClock.currentThreadTimeMillis();
- long pss = Debug.getPss(pid, tmp, null);
+ // skip background PSS calculation of apps that are capturing
+ // camera imagery
+ final boolean usingCamera = isCameraActiveForUid(proc.uid);
+ long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
long endTime = SystemClock.currentThreadTimeMillis();
synchronized (ActivityManagerService.this) {
if (pss != 0 && proc.thread != null && proc.setProcState == procState
@@ -2066,6 +2079,7 @@
ProcessList.abortNextPssTime(proc.procStateMemTracker);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
": " + (proc.thread == null ? "NO_THREAD " : "") +
+ (usingCamera ? "CAMERA " : "") +
(proc.pid != pid ? "PID_CHANGED " : "") +
" initState=" + procState + " curState=" +
proc.setProcState + " " +
@@ -2155,6 +2169,14 @@
}
}
});
+
+ final int[] cameraOp = {AppOpsManager.OP_CAMERA};
+ mAppOpsService.startWatchingActive(cameraOp, new IAppOpsActiveCallback.Stub() {
+ @Override
+ public void opActiveChanged(int op, int uid, String packageName, boolean active) {
+ cameraActiveChanged(uid, active);
+ }
+ });
}
public void setWindowManager(WindowManagerService wm) {
@@ -18273,6 +18295,27 @@
}
}
+ final void cameraActiveChanged(@UserIdInt int uid, boolean active) {
+ synchronized (mActiveCameraUids) {
+ final int curIndex = mActiveCameraUids.indexOf(uid);
+ if (active) {
+ if (curIndex < 0) {
+ mActiveCameraUids.add(uid);
+ }
+ } else {
+ if (curIndex >= 0) {
+ mActiveCameraUids.remove(curIndex);
+ }
+ }
+ }
+ }
+
+ final boolean isCameraActiveForUid(@UserIdInt int uid) {
+ synchronized (mActiveCameraUids) {
+ return mActiveCameraUids.indexOf(uid) >= 0;
+ }
+ }
+
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6ad1a5..1038069 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2050,7 +2050,9 @@
final int pid = precedence.pid;
long now = System.currentTimeMillis();
final long end = now + PROC_KILL_TIMEOUT;
+ final int oldPolicy = StrictMode.getThreadPolicyMask();
try {
+ StrictMode.setThreadPolicyMask(0);
Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
// It's killed successfully, but we'd make sure the cleanup work is done.
synchronized (precedence) {
@@ -2069,9 +2071,11 @@
}
}
} catch (Exception e) {
- // It's still alive...
+ // It's still alive... maybe blocked at uninterruptible sleep ?
Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
- + app);
+ + app, e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldPolicy);
}
}
try {
@@ -2416,7 +2420,15 @@
ProcessList.killProcessGroup(app.uid, app.pid);
checkSlow(startTime, "startProcess: done killing old proc");
- Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ || app.lastCachedPss < getCachedRestoreThresholdKb()) {
+ // Throw a wtf if it's not killed, or killed but not because the system was in
+ // memory pressure + the app was in "cch-empty" and used large amount of memory
+ Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ } else {
+ Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ }
// We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
// routine of it yet, but we'd set it as the precedence of the new process.
precedence = app;
@@ -2819,7 +2831,15 @@
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
- Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ if (old.killed) {
+ // The old process has been killed, we probably haven't had
+ // a chance to clean up the old record, just log a warning
+ Slog.w(TAG, "Existing proc " + old + " was killed "
+ + (SystemClock.uptimeMillis() - old.mKillTime)
+ + "ms ago when adding " + proc);
+ } else {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 598b70e7..cd4302b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -350,6 +350,8 @@
boolean mReachable; // Whether or not this process is reachable from given process
+ long mKillTime; // The timestamp in uptime when this process was killed.
+
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startTime) {
this.startUid = startUid;
@@ -916,6 +918,7 @@
if (!mPersistent) {
killed = true;
killedByAm = true;
+ mKillTime = SystemClock.uptimeMillis();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 9562158..1bf761f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1891,8 +1891,7 @@
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
- if (mInjector.isCallerRecents(callingUid)
- && isSameProfileGroup(callingUserId, targetUserId)) {
+ if (mInjector.isCallerRecents(callingUid) && isSameProfileGroup) {
// If the caller is Recents and the caller has ownership of the profile group,
// we then allow it to access its profiles.
allow = true;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3ff6ec1..86e6a32 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -244,7 +244,7 @@
if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
return false;
}
- // Cache incoming message. Note that it caches only white-listed one.
+ // Cache incoming message if it is included in the list of cacheable opcodes.
mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index aed94fc..64d70d6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -225,7 +225,7 @@
if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE, true)) {
mWakeLock = new SystemWakeLock();
} else {
- // Create a dummy lock object that doesn't do anything about wake lock,
+ // Create a stub lock object that doesn't do anything about wake lock,
// hence allows the device to go to sleep even if it's the active source.
mWakeLock = new ActiveWakeLock() {
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 2c0ddaf..804cc92 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1667,6 +1667,7 @@
if (avr == null) {
return;
}
+ setArcStatus(false);
// Seq #44.
removeAction(RequestArcInitiationAction.class);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 30d7d54..a8a9a36 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2290,7 +2290,7 @@
pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
- pw.println("mHdmiCecVolumeControlEnabled " + mHdmiCecVolumeControlEnabled);
+ pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
pw.decreaseIndent();
pw.println("mMhlController: ");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0154fe0..254285d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2942,7 +2942,7 @@
vis = 0;
}
if (!mCurPerceptible) {
- vis = 0;
+ vis &= ~InputMethodService.IME_VISIBLE;
}
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f69c823..d933c10 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,6 +17,9 @@
package com.android.server.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_MOCK_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -120,10 +123,16 @@
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationAttributionHelper;
+import com.android.server.location.util.LocationPermissionsHelper;
+import com.android.server.location.util.LocationPowerSaveModeHelper;
import com.android.server.location.util.LocationUsageLogger;
+import com.android.server.location.util.ScreenInteractiveHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.SystemAppForegroundHelper;
import com.android.server.location.util.SystemAppOpsHelper;
+import com.android.server.location.util.SystemLocationPermissionsHelper;
+import com.android.server.location.util.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.util.SystemScreenInteractiveHelper;
import com.android.server.location.util.SystemSettingsHelper;
import com.android.server.location.util.SystemUserInfoHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -173,7 +182,7 @@
publishBinderService(Context.LOCATION_SERVICE, mService);
// client caching behavior is only enabled after seeing the first invalidate
- invalidateLocalLocationEnabledCaches();
+ LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
.disableLocalLocationEnabledCaches();
@@ -486,7 +495,7 @@
private void onLocationModeChanged(int userId) {
boolean enabled = mSettingsHelper.isLocationEnabled(userId);
- invalidateLocalLocationEnabledCaches();
+ LocationManager.invalidateLocalLocationEnabledCaches();
if (D) {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
@@ -1232,19 +1241,20 @@
if (!currentlyMonitoring) {
if (allowMonitoring) {
if (!highPower) {
- return mAppOpsHelper.startLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ mCallerIdentity);
}
}
} else {
- if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity,
+ if (!allowMonitoring || !mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
- mCallerIdentity.getPid()))) {
+ mCallerIdentity.getPid())), mCallerIdentity)) {
if (!highPower) {
- mAppOpsHelper.stopLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
}
return false;
}
@@ -1589,8 +1599,9 @@
continue;
}
- if (!mAppOpsHelper.checkLocationAccess(identity,
- record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
+ record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
+ identity)) {
continue;
}
final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
@@ -2118,7 +2129,8 @@
}
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
return null;
}
@@ -2179,7 +2191,8 @@
if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
// appops check should always be right before delivery
- if (mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+ if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
transport.deliverResult(lastLocation);
} else {
transport.deliverResult(null);
@@ -2329,7 +2342,7 @@
}
@Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ public void sendExtraCommand(String provider, String command, Bundle extras) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
mContext.enforceCallingOrSelfPermission(
permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
@@ -2350,8 +2363,6 @@
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
provider);
-
- return true;
}
@Override
@@ -2553,7 +2564,8 @@
r.mLastFixBroadcast = location;
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ receiver.mCallerIdentity)) {
continue;
}
@@ -2644,7 +2656,7 @@
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2664,7 +2676,7 @@
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2687,7 +2699,7 @@
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2708,7 +2720,7 @@
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2926,24 +2938,36 @@
private final UserInfoHelper mUserInfoHelper;
private final SystemAppOpsHelper mAppOpsHelper;
+ private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
private final SystemSettingsHelper mSettingsHelper;
private final SystemAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
SystemInjector(Context context, UserInfoHelper userInfoHelper) {
mUserInfoHelper = userInfoHelper;
mAppOpsHelper = new SystemAppOpsHelper(context);
+ mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
+ mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
+ mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
void onSystemReady() {
mAppOpsHelper.onSystemReady();
+ mLocationPermissionsHelper.onSystemReady();
mSettingsHelper.onSystemReady();
mAppForegroundHelper.onSystemReady();
+ mLocationPowerSaveModeHelper.onSystemReady();
+ mScreenInteractiveHelper.onSystemReady();
}
@Override
@@ -2957,6 +2981,11 @@
}
@Override
+ public LocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public SettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -2972,8 +3001,23 @@
}
@Override
+ public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public ScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
+ }
+
+ @Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index f37992a..f6896b8 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -50,14 +50,10 @@
Criteria.POWER_LOW,
Criteria.ACCURACY_COARSE);
- private volatile boolean mReportLocation;
-
public PassiveProvider(Context context) {
// using a direct executor is ok because this class has no locks that could deadlock
super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
- mReportLocation = false;
-
setProperties(PROPERTIES);
setAllowed(true);
}
@@ -66,15 +62,11 @@
* Pass a location into the passive provider.
*/
public void updateLocation(Location location) {
- if (mReportLocation) {
- reportLocation(location);
- }
+ reportLocation(location);
}
@Override
- public void onSetRequest(ProviderRequest request) {
- mReportLocation = request.reportLocation;
- }
+ public void onSetRequest(ProviderRequest request) {}
@Override
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index c855a12..2d9734e 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -16,7 +16,6 @@
package com.android.server.location.geofence;
-import static android.Manifest.permission;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -44,8 +43,8 @@
import com.android.server.location.LocationPermissions;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.PendingIntentListenerRegistration;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -86,7 +85,7 @@
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
private @Nullable Location mCachedLocation;
private float mCachedLocationDistanceM;
@@ -101,7 +100,7 @@
mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- TAG + ":" + identity.getPackageName());
+ TAG + ":" + identity.getPackageName());
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(identity.addToWorkSource(null));
}
@@ -114,7 +113,8 @@
@Override
protected void onPendingIntentListenerRegister() {
mGeofenceState = STATE_UNKNOWN;
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
}
@Override
@@ -127,18 +127,32 @@
}
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -186,10 +200,10 @@
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
try {
- pendingIntent.send(mContext, 0, intent,
- (pI, i, rC, rD, rE) -> mWakeLock.release(),
- null, permission.ACCESS_FINE_LOCATION,
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ // send() only enforces permissions for broadcast intents, but since clients can
+ // select any kind of pending intent we do not rely on send() to enforce permissions
+ pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
+ null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
} catch (PendingIntent.CanceledException e) {
mWakeLock.release();
removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
@@ -202,7 +216,7 @@
builder.append(getIdentity());
ArraySet<String> flags = new ArraySet<>(1);
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -224,10 +238,22 @@
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GeofenceManager.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GeofenceManager.this.onLocationPermissionsChanged(uid);
+ }
+ };
protected final UserInfoHelper mUserInfoHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final SettingsHelper mSettingsHelper;
protected final LocationUsageLogger mLocationUsageLogger;
@@ -241,7 +267,7 @@
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
}
@@ -281,7 +307,7 @@
@Override
protected boolean isActive(GeofenceRegistration registration) {
CallerIdentity identity = registration.getIdentity();
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
&& !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
@@ -294,7 +320,7 @@
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
}
@Override
@@ -303,7 +329,7 @@
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
}
@Override
@@ -434,7 +460,11 @@
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 53e660a..0b7968b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -31,8 +31,8 @@
import com.android.server.location.listeners.BinderListenerRegistration;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.util.AppForegroundHelper;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
import com.android.server.location.util.UserInfoHelper.UserListener;
@@ -65,7 +65,7 @@
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
private boolean mForeground;
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
protected GnssListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
@@ -84,24 +84,39 @@
return mForeground;
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
@Override
protected void onBinderListenerRegister() {
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -125,7 +140,7 @@
if (!mForeground) {
flags.add("bg");
}
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -141,7 +156,7 @@
protected final UserInfoHelper mUserInfoHelper;
protected final SettingsHelper mSettingsHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final AppForegroundHelper mAppForegroundHelper;
protected final LocationManagerInternal mLocationManagerInternal;
@@ -154,14 +169,26 @@
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
+ }
+ };
private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
this::onAppForegroundChanged;
protected GnssListenerMultiplexer(Injector injector) {
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
@@ -208,7 +235,7 @@
CallerIdentity identity = registration.getIdentity();
// TODO: this should be checking if the gps provider is enabled, not if location is enabled,
// but this is the same for now.
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& (registration.isForeground() || isBackgroundRestrictionExempt(identity))
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
@@ -241,7 +268,7 @@
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
}
@@ -257,7 +284,7 @@
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
}
@@ -279,8 +306,12 @@
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
private void onAppForegroundChanged(int uid, boolean foreground) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 8aaf4bf6..8004ec7 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -2008,9 +2008,7 @@
private final class FusedLocationListener extends LocationChangeListener {
@Override
public void onLocationChanged(Location location) {
- if (LocationManager.FUSED_PROVIDER.equals(location.getProvider())) {
- injectBestLocation(location);
- }
+ injectBestLocation(location);
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 58e725c..8e81f29 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -18,10 +18,9 @@
import static android.location.LocationManager.GPS_PROVIDER;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import android.Manifest;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.Context;
import android.location.GnssAntennaInfo;
import android.location.GnssMeasurementCorrections;
@@ -47,10 +46,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
-import com.android.server.location.util.SettingsHelper;
import java.io.FileDescriptor;
import java.util.List;
@@ -68,9 +65,7 @@
}
private final Context mContext;
- private final SettingsHelper mSettingsHelper;
private final AppOpsHelper mAppOpsHelper;
- private final AppForegroundHelper mAppForegroundHelper;
private final LocationManagerInternal mLocationManagerInternal;
private final GnssLocationProvider mGnssLocationProvider;
@@ -109,9 +104,7 @@
GnssNative.initialize();
mContext = context.createAttributionContext(ATTRIBUTION_ID);
- mSettingsHelper = injector.getSettingsHelper();
mAppOpsHelper = injector.getAppOpsHelper();
- mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
if (gnssLocationProvider == null) {
@@ -192,9 +185,10 @@
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
String attributionTag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
- if (!mAppOpsHelper.checkLocationAccess(identity, PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity)) {
return false;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 9227a17..0815d46 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssMeasurementsEvent;
import android.location.GnssRequest;
import android.location.IGnssMeasurementsListener;
@@ -30,6 +30,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
@@ -47,6 +48,7 @@
GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
SettingsHelper.GlobalSettingChangedListener {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
private final GnssMeasurementProviderNative mNative;
@@ -57,6 +59,7 @@
@VisibleForTesting
public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
mNative = aNative;
}
@@ -163,7 +166,8 @@
*/
public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssMeasurementsReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index a07fbe4..7dcffc6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssNavigationMessage;
import android.location.IGnssNavigationMessageListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
/**
@@ -39,6 +40,7 @@
public class GnssNavigationMessageProvider extends
GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final GnssNavigationMessageProviderNative mNative;
public GnssNavigationMessageProvider(Injector injector) {
@@ -49,6 +51,7 @@
public GnssNavigationMessageProvider(Injector injector,
GnssNavigationMessageProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mNative = aNative;
}
@@ -90,7 +93,8 @@
*/
public void onNavigationMessageAvailable(GnssNavigationMessage event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssNavigationMessageReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index d33b058..19f7927 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssStatus;
import android.location.IGnssStatusListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@
import android.stats.location.LocationStatsEnums;
import android.util.Log;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
@@ -35,10 +36,12 @@
*/
public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
public GnssStatusProvider(Injector injector) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
}
@@ -113,7 +116,8 @@
*/
public void onSvStatusChanged(GnssStatus gnssStatus) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onSvStatusChanged(gnssStatus);
} else {
return null;
@@ -126,7 +130,8 @@
*/
public void onNmeaReceived(long timestamp, String nmea) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onNmeaReceived(timestamp, nmea);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f5889ce..528cf8a 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -21,6 +21,7 @@
import android.os.Binder;
import android.os.Build;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -40,8 +41,8 @@
* A base class to multiplex client listener registrations within system server. Registrations are
* divided into two categories, active registrations and inactive registrations, as defined by
* {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
- * {@link #updateRegistrations(Predicate)} or {@link #updateRegistration(Object, Predicate)} must be
- * invoked and return true for any registration whose active state may have changed.
+ * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
+ * whose active state may have changed.
*
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
*
@@ -217,7 +218,6 @@
mRegistrations.put(key, registration);
}
-
if (wasEmpty) {
onRegister();
}
@@ -268,7 +268,8 @@
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TKey key = mRegistrations.keyAt(i);
if (predicate.test(key)) {
removeRegistration(key, mRegistrations.valueAt(i));
@@ -287,7 +288,7 @@
* completely at some later time.
*/
protected final void removeRegistration(@NonNull Object key,
- @NonNull ListenerRegistration registration) {
+ @NonNull ListenerRegistration<?, ?> registration) {
synchronized (mRegistrations) {
int index = mRegistrations.indexOfKey(key);
if (index < 0) {
@@ -353,7 +354,8 @@
}
ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
actives.add(registration);
@@ -395,7 +397,8 @@
protected final void updateService(Predicate<TRegistration> predicate) {
synchronized (mRegistrations) {
boolean updateService = false;
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (predicate.test(registration) && registration.isActive()) {
updateService = true;
@@ -434,7 +437,8 @@
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (predicate.test(registration)) {
onRegistrationActiveChanged(registration);
@@ -446,33 +450,6 @@
}
}
- /**
- * Evaluates the predicate on the registration with the given key. The predicate should return
- * true if the active state of the registration may have changed as a result. Any
- * {@link #updateService()} invocations made while this method is executing will be deferred
- * until after the method is complete so as to avoid redundant work.
- */
- protected final void updateRegistration(TKey key, @NonNull Predicate<TRegistration> predicate) {
- synchronized (mRegistrations) {
- // since updating a registration can invoke a variety of callbacks, we need to ensure
- // those callbacks themselves do not re-enter, as this could lead to out-of-order
- // callbacks. note that try-with-resources ordering is meaningful here as well. we want
- // to close the reentrancy guard first, as this may generate additional service updates,
- // then close the update service buffer.
- long identity = Binder.clearCallingIdentity();
- try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
- ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
-
- TRegistration registration = mRegistrations.get(key);
- if (registration != null && predicate.test(registration)) {
- onRegistrationActiveChanged(registration);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
@GuardedBy("mRegistrations")
private void onRegistrationActiveChanged(TRegistration registration) {
if (Build.IS_DEBUGGABLE) {
@@ -511,7 +488,8 @@
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
ListenerOperation<TListener> operation = function.apply(registration);
@@ -537,7 +515,8 @@
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
execute(registration, operation);
@@ -571,7 +550,8 @@
ipw.println("listeners:");
ipw.increaseIndent();
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
ipw.print(registration);
if (!registration.isActive()) {
@@ -612,23 +592,33 @@
*/
private final class ReentrancyGuard implements AutoCloseable {
+ @GuardedBy("mRegistrations")
private int mGuardCount;
- private @Nullable ArrayList<Pair<Object, ListenerRegistration>> mScheduledRemovals;
+ @GuardedBy("mRegistrations")
+ private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
ReentrancyGuard() {
mGuardCount = 0;
mScheduledRemovals = null;
}
+ @GuardedBy("mRegistrations")
boolean isReentrant() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
return mGuardCount != 0;
}
- void markForRemoval(Object key, ListenerRegistration registration) {
+ @GuardedBy("mRegistrations")
+ void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
Preconditions.checkState(isReentrant());
if (mScheduledRemovals == null) {
- mScheduledRemovals = new ArrayList<>(mRegistrations.size());
+ mScheduledRemovals = new ArraySet<>(mRegistrations.size());
}
mScheduledRemovals.add(new Pair<>(key, registration));
}
@@ -640,7 +630,7 @@
@Override
public void close() {
- ArrayList<Pair<Object, ListenerRegistration>> scheduledRemovals = null;
+ ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
Preconditions.checkState(mGuardCount > 0);
if (--mGuardCount == 0) {
@@ -650,8 +640,10 @@
if (scheduledRemovals != null) {
try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
- for (int i = 0; i < scheduledRemovals.size(); i++) {
- Pair<Object, ListenerRegistration> pair = scheduledRemovals.get(i);
+ final int size = scheduledRemovals.size();
+ for (int i = 0; i < size; i++) {
+ Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
+ i);
removeRegistration(pair.first, pair.second);
}
}
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index e529a7d..6a815ea 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -61,7 +61,7 @@
* Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
* after {@link #onUnregister()}. It is safe to invoke this from within either function.
*/
- public void remove() {
+ public final void remove() {
Object key = mKey;
if (key != null) {
getOwner().removeRegistration(key, this);
diff --git a/services/core/java/com/android/server/location/util/AppOpsHelper.java b/services/core/java/com/android/server/location/util/AppOpsHelper.java
index 3e42f27..1578289 100644
--- a/services/core/java/com/android/server/location/util/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/AppOpsHelper.java
@@ -16,15 +16,8 @@
package com.android.server.location.util;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import android.app.AppOpsManager;
import android.location.util.identity.CallerIdentity;
-import com.android.server.location.LocationPermissions;
-import com.android.server.location.LocationPermissions.PermissionLevel;
-
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -70,84 +63,27 @@
}
/**
- * Checks if the given identity may have locations delivered without noting that a location is
- * being delivered. This is a looser guarantee than
- * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package
- * arguments and so should not be used with unvalidated arguments or before actually delivering
- * locations.
- *
- * @see AppOpsManager#checkOpNoThrow(int, int, String)
+ * Starts the given appop.
*/
- public final boolean checkLocationAccess(CallerIdentity callerIdentity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), callerIdentity);
- }
+ public abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notes location access to the given identity, ie, location delivery. This method should be
- * called right before a location is delivered, and if it returns false, the location should not
- * be delivered.
+ * Finishes the given appop.
*/
- public final boolean noteLocationAccess(CallerIdentity identity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
- }
+ public abstract void finishOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is using location at normal/low power levels. If
- * this function returns false, do not later call
- * {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Checks the given appop.
*/
- public final boolean startLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_LOCATION, identity);
- }
+ public abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is no longer using location at normal/low power
- * levels.
+ * Notes the given appop (and may throw a security exception).
*/
- public final void stopLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_LOCATION, identity);
- }
+ public abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is using location at high levels. If this function
- * returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Notes the given appop.
*/
- public final boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
-
- /**
- * Notifies app ops that the given identity is no longer using location at high power levels.
- */
- public final void stopHighPowerLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
-
- /**
- * Notes access to any mock location APIs. If this call returns false, access to the APIs should
- * silently fail.
- */
- public final boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
- return noteOp(AppOpsManager.OP_MOCK_LOCATION, callerIdentity);
- }
-
- protected abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract void finishOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
+ public abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
}
diff --git a/services/core/java/com/android/server/location/util/Injector.java b/services/core/java/com/android/server/location/util/Injector.java
index e16df5d..379b303 100644
--- a/services/core/java/com/android/server/location/util/Injector.java
+++ b/services/core/java/com/android/server/location/util/Injector.java
@@ -17,6 +17,7 @@
package com.android.server.location.util;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.LocationRequestStatistics;
/**
* Injects various location dependencies so that they may be controlled by tests.
@@ -30,15 +31,27 @@
/** Returns an AppOpsHelper. */
AppOpsHelper getAppOpsHelper();
+ /** Returns a LocationPermissionsHelper. */
+ LocationPermissionsHelper getLocationPermissionsHelper();
+
/** Returns a SettingsHelper. */
SettingsHelper getSettingsHelper();
/** Returns an AppForegroundHelper. */
AppForegroundHelper getAppForegroundHelper();
- /** Returns a LocationUsageLogger. */
- LocationUsageLogger getLocationUsageLogger();
+ /** Returns a LocationPowerSaveModeHelper. */
+ LocationPowerSaveModeHelper getLocationPowerSaveModeHelper();
+
+ /** Returns a ScreenInteractiveHelper. */
+ ScreenInteractiveHelper getScreenInteractiveHelper();
/** Returns a LocationAttributionHelper. */
LocationAttributionHelper getLocationAttributionHelper();
+
+ /** Returns a LocationUsageLogger. */
+ LocationUsageLogger getLocationUsageLogger();
+
+ /** Returns a LocationRequestStatistics. */
+ LocationRequestStatistics getLocationRequestStatistics();
}
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index 8fe0941..bc3ac0f 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -16,9 +16,16 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
import android.location.util.identity.CallerIdentity;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -83,7 +90,7 @@
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startLocationMonitoring(identity)) {
+ if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
mAttributions.remove(identity);
}
}
@@ -99,7 +106,7 @@
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
mAttributions.remove(identity);
- mAppOpsHelper.stopLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
}
}
@@ -113,14 +120,18 @@
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startHighPowerLocationMonitoring(identity)) {
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+ if (D) {
+ Log.v(TAG, "starting high power location attribution for " + identity);
+ }
+ } else {
mHighPowerAttributions.remove(identity);
}
}
}
/**
- * Report high power location usage has stopped for the given caller on the given provider,
+ * Report high power location usage has stopped for the given caller on the given provider,
* with a unique key.
*/
public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
@@ -128,8 +139,11 @@
Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
+ if (D) {
+ Log.v(TAG, "stopping high power location attribution for " + identity);
+ }
mHighPowerAttributions.remove(identity);
- mAppOpsHelper.stopHighPowerLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
}
}
}
diff --git a/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
new file mode 100644
index 0000000..daf5679
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+
+import android.location.util.identity.CallerIdentity;
+
+import com.android.server.location.LocationPermissions;
+import com.android.server.location.LocationPermissions.PermissionLevel;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides helpers and listeners for appops.
+ */
+public abstract class LocationPermissionsHelper {
+
+ /**
+ * Listener for current user changes.
+ */
+ public interface LocationPermissionsListener {
+
+ /**
+ * Called when something has changed about location permissions for the given package.
+ */
+ void onLocationPermissionsChanged(String packageName);
+
+ /**
+ * Called when something has changed about location permissions for the given uid.
+ */
+ void onLocationPermissionsChanged(int uid);
+ }
+
+ private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
+ private final AppOpsHelper mAppOps;
+
+ public LocationPermissionsHelper(AppOpsHelper appOps) {
+ mListeners = new CopyOnWriteArrayList<>();
+ mAppOps = appOps;
+
+ mAppOps.addListener(this::onAppOpsChanged);
+ }
+
+ protected final void notifyLocationPermissionsChanged(String packageName) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(packageName);
+ }
+ }
+
+ protected final void notifyLocationPermissionsChanged(int uid) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(uid);
+ }
+ }
+
+ private void onAppOpsChanged(String packageName) {
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ /**
+ * Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
+ */
+ public final void addListener(LocationPermissionsListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for location permissions events.
+ */
+ public final void removeListener(LocationPermissionsListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Returns true if the given identity may access location at the given permissions level, taking
+ * into account both permissions and appops.
+ */
+ public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
+ CallerIdentity identity) {
+ if (permissionLevel == PERMISSION_NONE) {
+ return false;
+ }
+
+ if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
+ return false;
+ }
+
+ return mAppOps.checkOpNoThrow(permissionLevel, identity);
+ }
+
+ protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
+}
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..a9a8c50
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.os.PowerManager.LocationPowerSaveMode;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public abstract class LocationPowerSaveModeHelper {
+
+ /**
+ * Listener for location power save mode changes.
+ */
+ public interface LocationPowerSaveModeChangedListener {
+ /**
+ * Called when the location power save mode changes.
+ */
+ void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
+ }
+
+ private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
+
+ public LocationPowerSaveModeHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to location power save mode. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to location power save mode.
+ */
+ public final void removeListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyLocationPowerSaveModeChanged(
+ @LocationPowerSaveMode int locationPowerSaveMode) {
+ for (LocationPowerSaveModeChangedListener listener : mListeners) {
+ listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+ }
+
+ /**
+ * Returns the current location power save mode.
+ */
+ @LocationPowerSaveMode
+ public abstract int getLocationPowerSaveMode();
+}
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
new file mode 100644
index 0000000..d47bce3
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public abstract class ScreenInteractiveHelper {
+
+ /**
+ * Listener for screen interactive changes.
+ */
+ public interface ScreenInteractiveChangedListener {
+ /**
+ * Called when the screen interative state changes.
+ */
+ void onScreenInteractiveChanged(boolean isInteractive);
+ }
+
+ private final CopyOnWriteArrayList<ScreenInteractiveChangedListener> mListeners;
+
+ public ScreenInteractiveHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to screen interactive state. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(ScreenInteractiveChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to screen interactive state.
+ */
+ public final void removeListener(ScreenInteractiveChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyScreenInteractiveChanged(boolean interactive) {
+ for (ScreenInteractiveChangedListener listener : mListeners) {
+ listener.onScreenInteractiveChanged(interactive);
+ }
+ }
+
+ /**
+ * Returns true if the screen is currently interactive, and false otherwise.
+ */
+ public abstract boolean isInteractive();
+}
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index a953836..cfb7697 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -57,7 +57,7 @@
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -75,7 +75,7 @@
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -91,7 +91,7 @@
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -106,7 +106,7 @@
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -123,7 +123,7 @@
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
new file mode 100644
index 0000000..b9c0dde
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.Context;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for location permissions, including appops.
+ */
+public class SystemLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final Context mContext;
+
+ private boolean mInited;
+
+ public SystemLocationPermissionsHelper(Context context, AppOpsHelper appOps) {
+ super(appOps);
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mInited) {
+ return;
+ }
+
+ mContext.getPackageManager().addOnPermissionsChangeListener(
+ uid -> {
+ // invoked on ui thread, move to fg thread so ui thread isn't blocked
+ FgThread.getHandler().post(() -> notifyLocationPermissionsChanged(uid));
+ });
+ mInited = true;
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mContext.checkPermission(permission, callerIdentity.getPid(),
+ callerIdentity.getUid()) == PERMISSION_GRANTED;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..c8d8202
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper implements
+ Consumer<PowerSaveState> {
+
+ private final Context mContext;
+ private boolean mReady;
+
+ @LocationPowerSaveMode
+ private volatile int mLocationPowerSaveMode;
+
+ public SystemLocationPowerSaveModeHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
+ PowerManager.ServiceType.LOCATION, this);
+ mLocationPowerSaveMode = Objects.requireNonNull(
+ mContext.getSystemService(PowerManager.class)).getLocationPowerSaveMode();
+
+ mReady = true;
+ }
+
+ @Override
+ public void accept(PowerSaveState powerSaveState) {
+ int locationPowerSaveMode;
+ if (!powerSaveState.batterySaverEnabled) {
+ locationPowerSaveMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+ } else {
+ locationPowerSaveMode = powerSaveState.locationMode;
+ }
+
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+
+ // invoked on ui thread, move to fg thread so we don't block the ui thread
+ FgThread.getHandler().post(() -> notifyLocationPowerSaveModeChanged(locationPowerSaveMode));
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ Preconditions.checkState(mReady);
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
new file mode 100644
index 0000000..70cedac
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private final Context mContext;
+
+ private boolean mReady;
+
+ private volatile boolean mIsInteractive;
+
+ public SystemScreenInteractiveHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ IntentFilter screenIntentFilter = new IntentFilter();
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean interactive;
+ if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+ interactive = true;
+ } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ interactive = false;
+ } else {
+ return;
+ }
+
+ onScreenInteractiveChanged(interactive);
+ }
+ }, UserHandle.ALL, screenIntentFilter, null, FgThread.getHandler());
+
+ mReady = true;
+ }
+
+ private void onScreenInteractiveChanged(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ @Override
+ public boolean isInteractive() {
+ Preconditions.checkState(mReady);
+ return mIsInteractive;
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0eba69e..9f6c18d 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -26,8 +26,8 @@
import static com.android.server.media.MediaKeyDispatcher.isTripleTapOverridden;
import android.app.ActivityManager;
-import android.app.INotificationManager;
import android.app.KeyguardManager;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -68,7 +68,6 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
@@ -118,7 +117,7 @@
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock;
- private final INotificationManager mNotificationManager;
+ private final NotificationManager mNotificationManager;
private final Object mLock = new Object();
private final HandlerThread mRecordThread = new HandlerThread("SessionRecordThread");
// Keeps the full user id for each user.
@@ -158,10 +157,9 @@
super(context);
mContext = context;
mSessionManagerImpl = new SessionManagerImpl();
- PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
- mNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
}
@Override
@@ -507,11 +505,12 @@
private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
int resolvedUserId) {
if (hasStatusBarServicePermission(pid, uid)) return;
+ // TODO: Refactor to use hasMediaControlPermission and hasEnabledNotificationListener
if (mContext
.checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
!= PackageManager.PERMISSION_GRANTED
&& !isEnabledNotificationListener(compName,
- UserHandle.getUserHandleForUid(uid).getIdentifier(), resolvedUserId)) {
+ UserHandle.getUserHandleForUid(uid), resolvedUserId)) {
throw new SecurityException("Missing permission to control media.");
}
}
@@ -547,13 +546,13 @@
* they're running as.
*
* @param compName The component that is enabled.
- * @param userId The user id of the caller.
+ * @param userHandle The user handle of the caller.
* @param forUserId The user id they're making the request on behalf of.
* @return True if the component is enabled, false otherwise
*/
- private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+ private boolean isEnabledNotificationListener(ComponentName compName, UserHandle userHandle,
int forUserId) {
- if (userId != forUserId) {
+ if (userHandle.getIdentifier() != forUserId) {
// You may not access another user's content as an enabled listener.
return false;
}
@@ -561,12 +560,8 @@
Log.d(TAG, "Checking if enabled notification listener " + compName);
}
if (compName != null) {
- try {
- return mNotificationManager.isNotificationListenerAccessGrantedForUser(
- compName, userId);
- } catch (RemoteException e) {
- Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
- }
+ return mNotificationManager.hasEnabledNotificationListener(compName.getPackageName(),
+ userHandle);
}
return false;
}
@@ -1922,8 +1917,8 @@
* @param controllerUid uid of the controller app
*/
@Override
- public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
- throws RemoteException {
+ public boolean isTrusted(String controllerPackageName, int controllerPid,
+ int controllerUid) {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final long token = Binder.clearCallingIdentity();
@@ -1937,7 +1932,7 @@
// Context#getPackageName() for getting package name that matches with the PID/UID,
// but it doesn't tell which package has created the MediaController, so useless.
return hasMediaControlPermission(controllerPid, controllerUid)
- || hasEnabledNotificationListener(userId, controllerPackageName);
+ || hasEnabledNotificationListener(userId, controllerPackageName, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2001,28 +1996,20 @@
return resolvedUserId;
}
- private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName)
- throws RemoteException {
+ private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName,
+ int uid) {
+ // TODO: revisit this checking code
// You may not access another user's content as an enabled listener.
final int userId = UserHandle.getUserHandleForUid(resolvedUserId).getIdentifier();
if (resolvedUserId != userId) {
return false;
}
-
- // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
- // String pkgName) to notification team for optimization
- final List<ComponentName> enabledNotificationListeners =
- mNotificationManager.getEnabledNotificationListeners(userId);
- if (enabledNotificationListeners != null) {
- for (int i = 0; i < enabledNotificationListeners.size(); i++) {
- if (TextUtils.equals(packageName,
- enabledNotificationListeners.get(i).getPackageName())) {
- return true;
- }
- }
+ if (mNotificationManager.hasEnabledNotificationListener(packageName,
+ UserHandle.getUserHandleForUid(uid))) {
+ return true;
}
if (DEBUG) {
- Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled "
+ Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
+ "notification listener");
}
return false;
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index b460cb5..2e2d812 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,6 +2,7 @@
hdmoon@google.com
insun@google.com
jaewan@google.com
+jinpark@google.com
klhyun@google.com
lajos@google.com
sungsoo@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index deea589..cf48e02 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -516,7 +516,7 @@
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
- private static final IBinder WHITELIST_TOKEN = new Binder();
+ private static final IBinder ALLOWLIST_TOKEN = new Binder();
protected RankingHandler mRankingHandler;
private long mLastOverRateLogTime;
private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -1723,7 +1723,7 @@
super(context);
mNotificationRecordLogger = notificationRecordLogger;
mNotificationInstanceIdSequence = notificationInstanceIdSequence;
- Notification.processWhitelistToken = WHITELIST_TOKEN;
+ Notification.processAllowlistToken = ALLOWLIST_TOKEN;
}
// TODO - replace these methods with new fields in the VisibleForTesting constructor
@@ -4754,6 +4754,12 @@
}
@Override
+ public boolean hasEnabledNotificationListener(String packageName, int userId) {
+ checkCallerIsSystem();
+ return mListeners.isPackageAllowed(packageName, userId);
+ }
+
+ @Override
public boolean isNotificationListenerAccessGranted(ComponentName listener) {
Objects.requireNonNull(listener);
checkCallerIsSystemOrSameApp(listener.getPackageName());
@@ -5777,21 +5783,21 @@
mShortcutHelper.cacheShortcut(info, user);
}
- // Whitelist pending intents.
+ // temporarily allow apps to perform extra work when their pending intents are launched
if (notification.allPendingIntents != null) {
final int intentCount = notification.allPendingIntents.size();
if (intentCount > 0) {
final ActivityManagerInternal am = LocalServices
.getService(ActivityManagerInternal.class);
final long duration = LocalServices.getService(
- DeviceIdleInternal.class).getNotificationWhitelistDuration();
+ DeviceIdleInternal.class).getNotificationAllowlistDuration();
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
- WHITELIST_TOKEN, duration);
+ ALLOWLIST_TOKEN, duration);
am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
- WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
+ ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
| FLAG_SERVICE_SENDER));
}
}
@@ -7644,7 +7650,7 @@
// make sure deleteIntent cannot be used to start activities from background
LocalServices.getService(ActivityManagerInternal.class)
.clearPendingIntentAllowBgActivityStarts(deleteIntent.getTarget(),
- WHITELIST_TOKEN);
+ ALLOWLIST_TOKEN);
deleteIntent.send();
} catch (PendingIntent.CanceledException ex) {
// do nothing - there's no relevant way to recover, and
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
index 057f0f1..9b046b1 100644
--- a/services/core/java/com/android/server/notification/RankingReconsideration.java
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.java
@@ -90,7 +90,7 @@
/**
* Apply any computed changes to the notification record. This method will be
- * called on the main service thread, synchronized on he mNotificationList.
+ * called on the main service thread, synchronized on the mNotificationList.
* @param record The locked record to be updated.
*/
public abstract void applyChangesLocked(NotificationRecord record);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 00cb22e..391a08d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -459,12 +459,8 @@
final int returnCode = args.argi1;
args.recycle();
- final boolean showNotification;
- synchronized (mLock) {
- showNotification = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
- }
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
- showNotification, userId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
packageName, returnCode, message, extras);
break;
@@ -494,8 +490,11 @@
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
- @GuardedBy("mLock")
- private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
+ private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() {
+ assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
+ assertSealed("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
if (userId != UserHandle.getUserId(mInstallerUid)) {
return false;
}
@@ -513,12 +512,17 @@
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
- @GuardedBy("mLock")
- private boolean needToAskForPermissionsLocked() {
- if (mPermissionsManuallyAccepted) {
- return false;
+ private boolean needToAskForPermissions() {
+ final String packageName;
+ synchronized (mLock) {
+ if (mPermissionsManuallyAccepted) {
+ return false;
+ }
+ packageName = mPackageName;
}
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
final boolean isInstallPermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
@@ -528,7 +532,7 @@
final boolean isUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
- final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
+ final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && targetPackageUid != -1)
|| (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
@@ -540,7 +544,7 @@
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
- || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
+ || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner());
}
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
@@ -740,6 +744,18 @@
}
}
+ private void assertNotLocked(String cookie) {
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException(cookie + " is holding mLock");
+ }
+ }
+
+ private void assertSealed(String cookie) {
+ if (!isSealed()) {
+ throw new IllegalStateException(cookie + " before sealing");
+ }
+ }
+
@GuardedBy("mLock")
private void assertPreparedAndNotSealedLocked(String cookie) {
assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
@@ -1693,11 +1709,7 @@
}
private void handleInstall() {
- final boolean needsLogging;
- synchronized (mLock) {
- needsLogging = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
- }
- if (needsLogging) {
+ if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(mInstallSource.installerPackageName)
@@ -1724,9 +1736,7 @@
List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
try {
- synchronized (mLock) {
- installNonStagedLocked(childSessions);
- }
+ installNonStaged(childSessions);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -1735,11 +1745,10 @@
}
}
- @GuardedBy("mLock")
- private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
+ private void installNonStaged(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
- makeSessionActiveLocked();
+ makeSessionActive();
if (installingSession == null) {
return;
}
@@ -1752,7 +1761,7 @@
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession installingChildSession =
- session.makeSessionActiveLocked();
+ session.makeSessionActive();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
@@ -1762,8 +1771,12 @@
}
}
if (!success) {
- sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
- isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
@@ -1778,41 +1791,58 @@
* {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
* in case permissions need to be requested before install can proceed.
*/
+ private PackageManagerService.ActiveInstallSession makeSessionActive()
+ throws PackageManagerException {
+ assertNotLocked("makeSessionActive");
+
+ synchronized (mLock) {
+ if (mRelinquished) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session relinquished");
+ }
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session not sealed");
+ }
+ }
+
+ if (!params.isMultiPackage && needToAskForPermissions()) {
+ // User needs to confirm installation;
+ // give installer an intent they can use to involve
+ // user.
+ final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+ intent.setPackage(mPm.getPackageInstallerPackageName());
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+
+ // Commit was keeping session marked as active until now; release
+ // that extra refcount so session appears idle.
+ closeInternal(false);
+ return null;
+ }
+
+ synchronized (mLock) {
+ return makeSessionActiveLocked();
+ }
+ }
+
@GuardedBy("mLock")
private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
throws PackageManagerException {
- if (mRelinquished) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
- }
- if (mDestroyed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
- }
- if (!mSealed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
- }
-
if (!params.isMultiPackage) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
- if (needToAskForPermissionsLocked()) {
- // User needs to confirm installation;
- // give installer an intent they can use to involve
- // user.
- final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
- intent.setPackage(mPm.getPackageInstallerPackageName());
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-
- sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent);
-
- // Commit was keeping session marked as active until now; release
- // that extra refcount so session appears idle.
- closeInternal(false);
- return null;
- }
-
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
@@ -2438,7 +2468,7 @@
* Determine if creating hard links between source and destination is
* possible. That is, do they all live on the same underlying device.
*/
- private boolean isLinkPossible(List<File> fromFiles, File toDir) {
+ private static boolean isLinkPossible(List<File> fromFiles, File toDir) {
try {
final StructStat toStat = Os.stat(toDir.getAbsolutePath());
for (File fromFile : fromFiles) {
@@ -2915,7 +2945,12 @@
}
case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
// Don't fail or commit the session. Allow caller to commit again.
- sendPendingStreaming("DataLoader unavailable");
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId,
+ "DataLoader unavailable");
break;
}
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
@@ -2929,7 +2964,11 @@
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- sendPendingStreaming(e.getMessage());
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
}
}
};
@@ -3004,16 +3043,17 @@
detailMessage).sendToTarget();
}
+ @GuardedBy("mLock")
+ private int[] getChildSessionIdsLocked() {
+ final int[] childSessionIds = mChildSessionIds.copyKeys();
+ return childSessionIds != null ? childSessionIds : EMPTY_CHILD_SESSION_ARRAY;
+ }
+
@Override
public int[] getChildSessionIds() {
- final int[] childSessionIds;
synchronized (mLock) {
- childSessionIds = mChildSessionIds.copyKeys();
+ return getChildSessionIdsLocked();
}
- if (childSessionIds != null) {
- return childSessionIds;
- }
- return EMPTY_CHILD_SESSION_ARRAY;
}
private boolean canBeAddedAsChild(int parentCandidate) {
@@ -3323,6 +3363,9 @@
pw.decreaseIndent();
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnUserActionRequired(Context context, IntentSender target,
int sessionId, Intent intent) {
final Intent fillIn = new Intent();
@@ -3335,6 +3378,9 @@
}
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
boolean showNotification, int userId, String basePackageName, int returnCode,
String msg, Bundle extras) {
@@ -3375,13 +3421,12 @@
}
}
- private void sendPendingStreaming(@Nullable String cause) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
-
- if (statusReceiver == null) {
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
+ private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+ @Nullable String cause) {
+ if (target == null) {
Slog.e(TAG, "Missing receiver for pending streaming status.");
return;
}
@@ -3396,7 +3441,7 @@
intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
}
try {
- statusReceiver.sendIntent(mContext, 0, intent, null, null);
+ target.sendIntent(context, 0, intent, null, null);
} catch (IntentSender.SendIntentException ignored) {
}
}
@@ -3470,10 +3515,10 @@
if (stageCid != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
}
- writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
- writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
- writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
- writeBooleanAttribute(out, ATTR_SEALED, isSealed());
+ writeBooleanAttribute(out, ATTR_PREPARED, mPrepared);
+ writeBooleanAttribute(out, ATTR_COMMITTED, mCommitted);
+ writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed);
+ writeBooleanAttribute(out, ATTR_SEALED, mSealed);
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
@@ -3535,7 +3580,7 @@
params.appIconLastModified = appIconFile.lastModified();
}
- final int[] childSessionIds = getChildSessionIds();
+ final int[] childSessionIds = getChildSessionIdsLocked();
for (int childSessionId : childSessionIds) {
out.startTag(null, TAG_CHILD_SESSION);
writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
@@ -3543,7 +3588,7 @@
}
final InstallationFile[] files = getInstallationFilesLocked();
- for (InstallationFile file : getInstallationFilesLocked()) {
+ for (InstallationFile file : files) {
out.startTag(null, TAG_SESSION_FILE);
writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
writeStringAttribute(out, ATTR_NAME, file.getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bd12fd5..2a59893a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -651,6 +651,23 @@
private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
150857253;
+ /**
+ * Apps targeting Android S and above need to declare dependencies to the public native
+ * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+ * in its {@code AndroidManifest.xml}.
+ *
+ * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+ * the package manager rejects to install the app. The dependency can be specified as optional
+ * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+ * dependency doesn't stop the installation.
+ * <p>Once installed, an app is provided with only the native shared libraries that are
+ * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+ * in the app manifest will fail even if it actually exists on the device.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
+
public static final String PLATFORM_PACKAGE_NAME = "android";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
@@ -2952,9 +2969,8 @@
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
- String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
- addBuiltInSharedLibraryLocked(entry.filename, name);
+ addBuiltInSharedLibraryLocked(libConfig.valueAt(i));
}
// Now that we have added all the libraries, iterate again to add dependency
@@ -6429,9 +6445,14 @@
true /*allowDynamicSplits*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ final boolean queryMayBeFiltered =
+ UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
+ && !resolveForStart;
+
final ResolveInfo bestChoice =
chooseBestActivity(
- intent, resolvedType, flags, privateResolveFlags, query, userId);
+ intent, resolvedType, flags, privateResolveFlags, query, userId,
+ queryMayBeFiltered);
final boolean nonBrowserOnly =
(privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
@@ -6595,7 +6616,8 @@
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
- int flags, int privateResolveFlags, List<ResolveInfo> query, int userId) {
+ int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
+ boolean queryMayBeFiltered) {
if (query != null) {
final int N = query.size();
if (N == 1) {
@@ -6620,7 +6642,7 @@
// If we have saved a preference for a preferred activity for
// this Intent, use that.
ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
- flags, query, r0.priority, true, false, debug, userId);
+ flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered);
if (ri != null) {
return ri;
}
@@ -6802,11 +6824,19 @@
&& intent.hasCategory(CATEGORY_DEFAULT);
}
+ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> query, int priority, boolean always,
+ boolean removeMatches, boolean debug, int userId) {
+ return findPreferredActivityNotLocked(
+ intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId,
+ UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
+ }
+
// TODO: handle preferred activities missing while user has amnesia
/** <b>must not hold {@link #mLock}</b> */
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
- boolean removeMatches, boolean debug, int userId) {
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
if (Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
@@ -6900,10 +6930,12 @@
}
final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
&& !isDeviceProvisioned;
+ final boolean allowSetMutation = !excludeSetupWizardHomeActivity
+ && !queryMayBeFiltered;
if (ai == null) {
// Do not remove launcher's preferred activity during SetupWizard
// due to it may not install yet
- if (excludeSetupWizardHomeActivity) {
+ if (!allowSetMutation) {
continue;
}
@@ -6928,7 +6960,7 @@
continue;
}
- if (removeMatches) {
+ if (removeMatches && allowSetMutation) {
pir.removeFilter(pa);
changed = true;
if (DEBUG_PREFERRED) {
@@ -6945,7 +6977,7 @@
if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
- if (!excludeSetupWizardHomeActivity) {
+ if (allowSetMutation) {
// some components of the set are no longer present in
// the query, but the preferred activity can still be reused
if (DEBUG_PREFERRED) {
@@ -6966,24 +6998,28 @@
changed = true;
} else {
if (DEBUG_PREFERRED) {
- Slog.i(TAG, "Do not remove preferred activity for launcher"
- + " during SetupWizard");
+ Slog.i(TAG, "Do not remove preferred activity");
}
}
} else {
- Slog.i(TAG,
- "Result set changed, dropping preferred activity for "
- + intent + " type " + resolvedType);
- if (DEBUG_PREFERRED) {
- Slog.v(TAG, "Removing preferred activity since set changed "
- + pa.mPref.mComponent);
+ if (allowSetMutation) {
+ Slog.i(TAG,
+ "Result set changed, dropping preferred activity "
+ + "for " + intent + " type "
+ + resolvedType);
+ if (DEBUG_PREFERRED) {
+ Slog.v(TAG,
+ "Removing preferred activity since set changed "
+ + pa.mPref.mComponent);
+ }
+ pir.removeFilter(pa);
+ // Re-add the filter as a "last chosen" entry (!always)
+ PreferredActivity lastChosen = new PreferredActivity(
+ pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
+ false);
+ pir.addFilter(lastChosen);
+ changed = true;
}
- pir.removeFilter(pa);
- // Re-add the filter as a "last chosen" entry (!always)
- PreferredActivity lastChosen = new PreferredActivity(
- pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false);
- pir.addFilter(lastChosen);
- changed = true;
return null;
}
}
@@ -10486,6 +10522,19 @@
null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
+ // TODO(b/160928779) gate this behavior using ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES
+ if (pkg.getTargetSdkVersion() > 30) {
+ if (!pkg.getUsesNativeLibraries().isEmpty() && pkg.getTargetSdkVersion() > 30) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+ null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+ null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ }
return usesLibraryInfos;
}
@@ -12177,15 +12226,16 @@
}
@GuardedBy("mLock")
- private boolean addBuiltInSharedLibraryLocked(String path, String name) {
- if (nonStaticSharedLibExistsLocked(name)) {
+ private boolean addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
+ if (nonStaticSharedLibExistsLocked(entry.name)) {
return false;
}
- SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
- (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
- new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
- null, null);
+ SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
+ entry.name, (long) SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN,
+ new VersionedPackage(PLATFORM_PACKAGE_NAME, (long)0), null, null,
+ entry.isNative);
commitSharedLibraryInfoLocked(libraryInfo);
return true;
@@ -21900,7 +21950,11 @@
pw.print(" -> ");
}
if (libraryInfo.getPath() != null) {
- pw.print(" (jar) ");
+ if (libraryInfo.isNative()) {
+ pw.print(" (so) ");
+ } else {
+ pw.print(" (jar) ");
+ }
pw.print(libraryInfo.getPath());
} else {
pw.print(" (apk) ");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 13b927e..7106499 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4784,6 +4784,23 @@
}
}
+ List<String> usesNativeLibraries = pkg.getUsesNativeLibraries();
+ if (usesNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesNativeLibraries:");
+ for (int i=0; i< usesNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(usesNativeLibraries.get(i));
+ }
+ }
+
+ List<String> usesOptionalNativeLibraries = pkg.getUsesOptionalNativeLibraries();
+ if (usesOptionalNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesOptionalNativeLibraries:");
+ for (int i=0; i< usesOptionalNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" ");
+ pw.println(usesOptionalNativeLibraries.get(i));
+ }
+ }
+
List<String> usesLibraryFiles = ps.getPkgState().getUsesLibraryFiles();
if (usesLibraryFiles.size() > 0) {
pw.print(prefix); pw.println(" usesLibraryFiles:");
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index c9e0bb4..39784cf 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -252,6 +252,19 @@
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index ed57067..361a506 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -34,6 +34,7 @@
import android.os.ParcelableException;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.os.storage.StorageManagerInternal;
import android.os.storage.StorageVolume;
import android.service.storage.ExternalStorageService;
@@ -62,19 +63,25 @@
public final class StorageUserConnection {
private static final String TAG = "StorageUserConnection";
- public static final int REMOTE_TIMEOUT_SECONDS = 20;
+ private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20;
+ // TODO(b/161702661): Workaround for demo user to have shorter timeout.
+ // This allows the DevicePolicyManagerService#enableSystemApp call to succeed without ANR.
+ private static final int DEMO_USER_REMOTE_TIMEOUT_SECONDS = 5;
private final Object mLock = new Object();
private final Context mContext;
private final int mUserId;
private final StorageSessionController mSessionController;
private final ActiveConnection mActiveConnection = new ActiveConnection();
+ private final boolean mIsDemoUser;
@GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
mContext = Objects.requireNonNull(context);
mUserId = Preconditions.checkArgumentNonnegative(userId);
mSessionController = controller;
+ mIsDemoUser = LocalServices.getService(UserManagerInternal.class)
+ .getUserInfo(userId).isDemo();
}
/**
@@ -200,7 +207,8 @@
private void waitForLatch(CountDownLatch latch, String reason) throws TimeoutException {
try {
- if (!latch.await(REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+ if (!latch.await(mIsDemoUser ? DEMO_USER_REMOTE_TIMEOUT_SECONDS
+ : DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
// TODO(b/140025078): Call ActivityManager ANR API?
Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId);
throw new TimeoutException("Latch wait for " + reason + " elapsed");
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 2394baf..fd3c1f9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1469,7 +1469,7 @@
if (userId > 0) {
return userId;
} else {
- Slog.wtf(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
+ Log.w(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
return -100;
}
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index f5eed30..f5e1602 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -51,6 +51,7 @@
import android.app.GrantedUriPermission;
import android.app.IUriGrantsManager;
import android.content.ClipData;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -698,6 +699,11 @@
final UriPermission perm = findOrCreateUriPermissionLocked(
sourcePkg, targetPkg, targetUid, grantUri);
perm.initPersistedModes(modeFlags, createdTime);
+ mPmInternal.grantImplicitAccess(
+ targetUserId, null,
+ UserHandle.getAppId(targetUid),
+ pi.applicationInfo.uid,
+ false /* direct */);
}
} else {
Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -1171,6 +1177,9 @@
// grant, we can skip generating any bookkeeping; when any advanced
// features have been requested, we proceed below to make sure the
// provider supports granting permissions
+ mPmInternal.grantImplicitAccess(
+ UserHandle.getUserId(targetUid), null,
+ UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false);
return -1;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 55962fc..90f87b1 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1160,9 +1160,7 @@
}
};
- private Runnable mTryToRebindRunnable = () -> {
- tryToRebind();
- };
+ private Runnable mTryToRebindRunnable = this::tryToRebind;
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
@@ -1295,14 +1293,14 @@
// a short time in the future, specifically to allow any pending package
// update message on this same looper thread to be processed.
if (!mWallpaper.wallpaperUpdating) {
- mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+ mContext.getMainThreadHandler().postDelayed(mDisconnectRunnable,
1000);
}
}
}
}
- public void scheduleTimeoutLocked() {
+ private void scheduleTimeoutLocked() {
// If we didn't reset it right away, do so after we couldn't connect to
// it for an extended amount of time to avoid having a black wallpaper.
final Handler fgHandler = FgThread.getHandler();
@@ -1342,11 +1340,11 @@
}
}
- private void processDisconnect(final ServiceConnection connection) {
+ private Runnable mDisconnectRunnable = () -> {
synchronized (mLock) {
// The wallpaper disappeared. If this isn't a system-default one, track
// crashes and fall back to default if it continues to misbehave.
- if (connection == mWallpaper.connection) {
+ if (this == mWallpaper.connection) {
final ComponentName wpService = mWallpaper.wallpaperComponent;
if (!mWallpaper.wallpaperUpdating
&& mWallpaper.userId == mCurrentUserId
@@ -1374,7 +1372,7 @@
}
}
}
- }
+ };
/**
* Called by a live wallpaper if its colors have changed.
@@ -2786,6 +2784,13 @@
WallpaperConnection.DisplayConnector::disconnectLocked);
wallpaper.connection.mService = null;
wallpaper.connection.mDisplayConnector.clear();
+
+ FgThread.getHandler().removeCallbacks(wallpaper.connection.mResetRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mDisconnectRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mTryToRebindRunnable);
+
wallpaper.connection = null;
if (wallpaper == mLastWallpaper) mLastWallpaper = null;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 0cd7ffc..04b1edc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1446,7 +1446,9 @@
mService.deferWindowLayout();
try {
stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- stack.setBounds(null);
+ if (stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ stack.setBounds(null);
+ }
toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(stack);
// Follow on the workaround: activities are kept force hidden till the new windowing
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d766928..c4af3e2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -145,7 +145,6 @@
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
-import android.app.IRequestFinishCallback;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -2472,7 +2471,7 @@
}
@Override
- public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
+ public void onBackPressedOnTaskRoot(IBinder token) {
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
@@ -2486,18 +2485,13 @@
// callback
} else if (stack != null && (stack.isSingleTaskInstance())) {
// Single-task stacks are used for activities which are presented in floating
- // windows above full screen activities. Instead of directly finishing the
- // task, a task change listener is used to notify SystemUI so the action can be
- // handled specially.
+ // windows above full screen activities. A task change listener is used to notify
+ // SystemUI so the back action can be handled specially.
final Task task = r.getTask();
mTaskChangeNotificationController
.notifyBackPressedOnTaskRoot(task.getTaskInfo());
} else {
- try {
- callback.requestFinish();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke request finish callback", e);
- }
+ moveActivityTaskToBack(token, false /* nonRoot */);
}
}
}
@@ -2843,7 +2837,7 @@
}
if (toTop) {
- taskDisplayArea.positionStackAt(POSITION_TOP, primarySplitTask,
+ taskDisplayArea.positionChildAt(POSITION_TOP, primarySplitTask,
false /* includingParents */);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 98f57c5..0d8e882 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -368,7 +368,7 @@
/** Sets the always on top flag for this configuration container.
* When you call this function, make sure that the following functions are called as well to
* keep proper z-order.
- * - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
+ * - {@link TaskDisplayArea#positionChildAt(int POSITION_TOP, Task, boolean)};
* */
public void setAlwaysOnTop(boolean alwaysOnTop) {
mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index c7cba77..e2c0749 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.Task.TAG_VISIBILITY;
@@ -174,12 +172,7 @@
}
final int windowingMode = mContiner.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- // The visibility of tasks and the activities they contain in freeform stack are
- // determined individually unlike other stacks where the visibility or fullscreen
- // status of an activity in a previous task affects other.
- mBehindFullscreenActivity = !mContainerShouldBeVisible;
- } else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
+ if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
&& r.isRootOfTask()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
+ " stackShouldBeVisible=" + mContainerShouldBeVisible
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 3b39b6b..852b3672 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -88,6 +88,7 @@
mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.inputFeatures = 0;
mWindowHandle.scaleFactor = 1.0f;
+ mWindowHandle.trustedOverlay = true;
mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId).getSession())
.setContainerLayer()
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0216db4..20f1b9f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -27,7 +27,18 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -67,9 +78,6 @@
private boolean mUpdateInputWindowsPending;
private boolean mUpdateInputWindowsImmediately;
- // Currently focused input window handle.
- private InputWindowHandle mFocusedInputWindowHandle;
-
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
@@ -309,10 +317,6 @@
Slog.d(TAG_WM, "addInputWindowHandle: "
+ child + ", " + inputWindowHandle);
}
-
- if (hasFocus) {
- mFocusedInputWindowHandle = inputWindowHandle;
- }
}
void setUpdateInputWindowsNeededLw() {
@@ -578,6 +582,7 @@
inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
inputWindowHandle.touchableRegion.setEmpty();
inputWindowHandle.setTouchableRegionCrop(null);
+ inputWindowHandle.trustedOverlay = isTrustedOverlay(type);
}
/**
@@ -592,4 +597,17 @@
populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
t.setInputWindowInfo(sc, inputWindowHandle);
}
+
+ static boolean isTrustedOverlay(int type) {
+ return type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
+ || type == TYPE_INPUT_METHOD || type == TYPE_INPUT_METHOD_DIALOG
+ || type == TYPE_MAGNIFICATION_OVERLAY || type == TYPE_STATUS_BAR
+ || type == TYPE_NOTIFICATION_SHADE
+ || type == TYPE_NAVIGATION_BAR
+ || type == TYPE_NAVIGATION_BAR_PANEL
+ || type == TYPE_SECURE_SYSTEM_OVERLAY
+ || type == TYPE_DOCK_DIVIDER
+ || type == TYPE_ACCESSIBILITY_OVERLAY
+ || type == TYPE_INPUT_CONSUMER;
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e6e92ea..821bd70 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2459,7 +2459,7 @@
// Since always on top is only on when the stack is freeform or pinned, the state
// can be toggled when the windowing mode changes. We must make sure the stack is
// placed properly when always on top state changes.
- taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
}
}
@@ -5340,7 +5340,8 @@
}
if (isRootTask()) {
- taskDisplayArea.positionStackAtTop(this, false /* includingParents */, reason);
+ taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */,
+ reason);
}
if (task == null) {
task = this;
@@ -5368,7 +5369,8 @@
if (parentTask != null) {
parentTask.moveToBack(reason, this);
} else {
- displayArea.positionStackAtBottom(this, reason);
+ displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/,
+ reason);
}
if (task != null && task != this) {
positionChildAtBottom(task);
@@ -7448,7 +7450,7 @@
// always on top windows. Since the position the stack should be inserted into is calculated
// properly in {@link DisplayContent#getTopInsertPosition()} in both cases, we can just
// request that the stack is put at top here.
- taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
}
/** NOTE: Should only be called from {@link Task#reparent}. */
@@ -7493,7 +7495,7 @@
final Task task = getBottomMostTask();
setWindowingMode(WINDOWING_MODE_UNDEFINED);
- getDisplayArea().positionStackAtTop(this, false /* includingParents */);
+ getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4473bd6..246753a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -303,15 +303,18 @@
}
@Override
- void addChild(Task stack, int position) {
- if (DEBUG_STACK) Slog.d(TAG_WM, "Set stack=" + stack + " on taskDisplayArea=" + this);
- addStackReferenceIfNeeded(stack);
- position = findPositionForStack(position, stack, true /* adding */);
+ void addChild(Task task, int position) {
+ if (DEBUG_STACK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
+ if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1) {
+ throw new IllegalStateException("addChild: Can only have one task on display=" + this);
+ }
- super.addChild(stack, position);
+ addStackReferenceIfNeeded(task);
+ position = findPositionForStack(position, task, true /* adding */);
+
+ super.addChild(task, position);
mAtmService.updateSleepIfNeededLocked();
-
- positionStackAt(stack, position);
+ onStackOrderChanged(task);
}
@Override
@@ -328,19 +331,42 @@
return true;
}
+ void positionChildAt(int position, Task child, boolean includingParents,
+ String updateLastFocusedTaskReason) {
+ final Task prevFocusedTask = updateLastFocusedTaskReason != null ? getFocusedStack() : null;
+
+ positionChildAt(position, child, includingParents);
+
+ if (updateLastFocusedTaskReason == null) {
+ return;
+ }
+
+ final Task currentFocusedStack = getFocusedStack();
+ if (currentFocusedStack == prevFocusedTask) {
+ return;
+ }
+
+ mLastFocusedStack = prevFocusedTask;
+ EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
+ mDisplayContent.mDisplayId,
+ currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
+ mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
+ updateLastFocusedTaskReason);
+ }
+
@Override
void positionChildAt(int position, Task child, boolean includingParents) {
final boolean moveToTop = position >= getChildCount() - 1;
final boolean moveToBottom = position <= 0;
+ final int oldPosition = mChildren.indexOf(child);
if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
// This stack is always-on-top, override the default behavior.
Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
// Moving to its current position, as we must call super but we don't want to
// perform any meaningful action.
- final int currentPosition = mChildren.indexOf(child);
- super.positionChildAt(currentPosition, child, false /* includingParents */);
+ super.positionChildAt(oldPosition, child, false /* includingParents */);
return;
}
// We don't allow untrusted display to top when task stack moves to top,
@@ -358,7 +384,7 @@
child.updateTaskMovement(moveToTop);
- mDisplayContent.setLayoutNeeded();
+ mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
// The insert position may be adjusted to non-top when there is always-on-top stack. Since
// the original position is preferred to be top, the stack should have higher priority when
@@ -373,6 +399,10 @@
// Update the top resumed activity because the preferred top focusable task may be changed.
mAtmService.mStackSupervisor.updateTopResumedActivityIfNeeded();
+
+ if (mChildren.indexOf(child) != oldPosition) {
+ onStackOrderChanged(child);
+ }
}
@Override
@@ -800,66 +830,6 @@
}
}
- void positionStackAt(int position, Task child, boolean includingParents) {
- positionChildAt(position, child, includingParents);
- mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
- }
-
- void positionStackAtTop(Task stack, boolean includingParents) {
- positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAtTop(Task stack, boolean includingParents,
- String updateLastFocusedStackReason) {
- positionStackAt(stack, getStackCount(), includingParents,
- updateLastFocusedStackReason);
- }
-
- void positionStackAtBottom(Task stack) {
- positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAtBottom(Task stack, String updateLastFocusedStackReason) {
- positionStackAt(stack, 0, false /* includingParents */,
- updateLastFocusedStackReason);
- }
-
- void positionStackAt(Task stack, int position) {
- positionStackAt(stack, position, false /* includingParents */,
- null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAt(Task stack, int position, boolean includingParents,
- String updateLastFocusedStackReason) {
- // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
- // the position internally, also update the logic here
- final Task prevFocusedStack = updateLastFocusedStackReason != null
- ? getFocusedStack() : null;
- final boolean wasContained = mChildren.contains(stack);
- if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
- throw new IllegalStateException(
- "positionStackAt: Can only have one task on display=" + this);
- }
-
- // Since positionChildAt() is called during the creation process of pinned stacks,
- // ActivityStack#getStack() can be null.
- positionStackAt(position, stack, includingParents);
-
- if (updateLastFocusedStackReason != null) {
- final Task currentFocusedStack = getFocusedStack();
- if (currentFocusedStack != prevFocusedStack) {
- mLastFocusedStack = prevFocusedStack;
- EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
- mDisplayContent.mDisplayId,
- currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
- mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
- updateLastFocusedStackReason);
- }
- }
-
- onStackOrderChanged(stack);
- }
-
/**
* Moves/reparents `task` to the back of whatever container the home stack is in. This is for
* when we just want to move a task to "the back" vs. a specific place. The primary use-case
@@ -872,7 +842,7 @@
if (homeParentTask == null) {
// reparent throws if parent didn't change...
if (task.getParent() == this) {
- positionStackAtBottom(task);
+ positionChildAt(POSITION_BOTTOM, task, false /*includingParents*/);
} else {
task.reparent(this, false /* onTop */);
}
@@ -1091,7 +1061,7 @@
if (launchRootTask != null) {
launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
if (onTop) {
- positionStackAtTop(launchRootTask, false /* includingParents */);
+ positionChildAt(POSITION_TOP, launchRootTask, false /* includingParents */);
}
} else {
addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
@@ -1666,15 +1636,11 @@
return;
}
- final boolean isRootTask = stack.isRootTask();
- if (isRootTask) {
- // Move the stack to the bottom to not affect the following visibility checks
- positionStackAtBottom(stack);
- } else {
- stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
- }
+ // Move the stack to the bottom to not affect the following visibility checks
+ stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
// Find the next position where the stack should be placed
+ final boolean isRootTask = stack.isRootTask();
final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
final Task s = isRootTask ? getStackAt(stackNdx)
@@ -1688,11 +1654,7 @@
if (s.shouldBeVisible(null) && isValidWindowingMode) {
// Move the provided stack to behind this stack
final int position = Math.max(0, stackNdx - 1);
- if (isRootTask) {
- positionStackAt(stack, position);
- } else {
- stack.getParent().positionChildAt(position, stack, false /*includingParents */);
- }
+ stack.getParent().positionChildAt(position, stack, false /*includingParents */);
break;
}
}
@@ -1722,11 +1684,7 @@
final int insertIndex = stackIndex <= behindStackIndex
? behindStackIndex - 1 : behindStackIndex;
final int position = Math.max(0, insertIndex);
- if (stack.isRootTask()) {
- positionStackAt(stack, position);
- } else {
- parent.positionChildAt(position, stack, false /* includingParents */);
- }
+ parent.positionChildAt(position, stack, false /* includingParents */);
}
boolean hasPinnedTask() {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4c5ac93..548345e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -345,29 +345,17 @@
final Task rootTask = (Task) (
(newParent != null && !(newParent instanceof TaskDisplayArea))
? newParent : task.getRootTask());
- if (hop.getToTop()) {
- as.getDisplayArea().positionStackAtTop(rootTask,
- false /* includingParents */);
- } else {
- as.getDisplayArea().positionStackAtBottom(rootTask);
- }
+ as.getDisplayArea().positionChildAt(
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask,
+ false /* includingParents */);
}
} else {
throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task);
}
} else {
- // Ugh, of course ActivityStack has its own special reorder logic...
- if (task.isRootTask()) {
- if (hop.getToTop()) {
- as.getDisplayArea().positionStackAtTop(as, false /* includingParents */);
- } else {
- as.getDisplayArea().positionStackAtBottom(as);
- }
- } else {
- task.getParent().positionChildAt(
- hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
- task, false /* includingParents */);
- }
+ task.getParent().positionChildAt(
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ task, false /* includingParents */);
}
return TRANSACT_EFFECTS_LIFECYCLE;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9ab1572..f3c5092 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -63,7 +63,6 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
@@ -88,7 +87,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -954,17 +952,7 @@
mInputWindowHandle.trustedOverlay =
(mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
&& mOwnerCanAddInternalSystemWindow;
- mInputWindowHandle.trustedOverlay |=
- mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
- || mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG
- || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_STATUS_BAR
- || mAttrs.type == TYPE_NOTIFICATION_SHADE
- || mAttrs.type == TYPE_NAVIGATION_BAR
- || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL
- || mAttrs.type == TYPE_SECURE_SYSTEM_OVERLAY
- || mAttrs.type == TYPE_DOCK_DIVIDER
- || mAttrs.type == TYPE_ACCESSIBILITY_OVERLAY
- || mAttrs.type == TYPE_INPUT_CONSUMER;
+ mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type);
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6..7ec819f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3570,6 +3570,17 @@
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
+ /**
+ * Persist modified values to disk by calling {@link #saveSettingsLocked} for each
+ * affected user ID.
+ */
+ @GuardedBy("getLockObject()")
+ private void saveSettingsForUsersLocked(Set<Integer> affectedUserIds) {
+ for (int userId : affectedUserIds) {
+ saveSettingsLocked(userId);
+ }
+ }
+
private void saveSettingsLocked(int userHandle) {
DevicePolicyData policy = getUserData(userHandle);
JournaledFile journal = makeJournaledFile(userHandle);
@@ -4785,13 +4796,15 @@
/**
* Updates a flag that tells us whether the user's password currently satisfies the
- * requirements set by all of the user's active admins. The flag is updated both in memory
- * and persisted to disk by calling {@link #saveSettingsLocked}, for the value of the flag
- * be the correct one upon boot.
- * This should be called whenever the password or the admin policies have changed.
+ * requirements set by all of the user's active admins.
+ * This should be called whenever the password or the admin policies have changed. The caller
+ * is responsible for calling {@link #saveSettingsLocked} to persist the change.
+ *
+ * @return the set of user IDs that have been affected
*/
@GuardedBy("getLockObject()")
- private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ private Set<Integer> updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
final int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserData(credentialOwner);
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
@@ -4801,9 +4814,10 @@
metrics, userHandle, parent);
if (newCheckpoint != policy.mPasswordValidAtLastCheckpoint) {
policy.mPasswordValidAtLastCheckpoint = newCheckpoint;
- saveSettingsLocked(credentialOwner);
+ affectedUserIds.add(credentialOwner);
}
}
+ return affectedUserIds;
}
/**
@@ -6175,7 +6189,8 @@
}
}
- private void removeCaApprovalsIfNeeded(int userId) {
+ private Set<Integer> removeCaApprovalsIfNeeded(int userId) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
for (UserInfo userInfo : mUserManager.getProfiles(userId)) {
boolean isSecure = mLockPatternUtils.isSecure(userInfo.id);
if (userInfo.isManagedProfile()){
@@ -6184,11 +6199,12 @@
if (!isSecure) {
synchronized (getLockObject()) {
getUserData(userInfo.id).mAcceptedCaCertificates.clear();
- saveSettingsLocked(userInfo.id);
+ affectedUserIds.add(userInfo.id);
}
mCertificateMonitor.onCertificateApprovalsChanged(userId);
}
}
+ return affectedUserIds;
}
@Override
@@ -7458,42 +7474,45 @@
}
DevicePolicyData policy = getUserData(userId);
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
synchronized (getLockObject()) {
policy.mFailedPasswordAttempts = 0;
- updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
- saveSettingsLocked(userId);
- updatePasswordExpirationsLocked(userId);
+ affectedUserIds.add(userId);
+ affectedUserIds.addAll(updatePasswordValidityCheckpointLocked(
+ userId, /* parent */ false));
+ affectedUserIds.addAll(updatePasswordExpirationsLocked(userId));
setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
// Send a broadcast to each profile using this password as its primary unlock.
sendAdminCommandForLockscreenPoliciesLocked(
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
+
+ affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
+ saveSettingsForUsersLocked(affectedUserIds);
}
- removeCaApprovalsIfNeeded(userId);
}
/**
* Called any time the device password is updated. Resets all password expiration clocks.
+ *
+ * @return the set of user IDs that have been affected
*/
- private void updatePasswordExpirationsLocked(int userHandle) {
- ArraySet<Integer> affectedUserIds = new ArraySet<Integer>();
+ private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
userHandle, /* parent */ false);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
+ for (int i = 0; i < admins.size(); i++) {
ActiveAdmin admin = admins.get(i);
if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
affectedUserIds.add(admin.getUserHandle().getIdentifier());
long timeout = admin.passwordExpirationTimeout;
- long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
- admin.passwordExpirationDate = expiration;
+ admin.passwordExpirationDate =
+ timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
}
}
- for (int affectedUserId : affectedUserIds) {
- saveSettingsLocked(affectedUserId);
- }
+ return affectedUserIds;
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
index da794da..e947e89 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
@@ -57,7 +57,7 @@
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
@@ -68,20 +68,20 @@
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
Preconditions.checkState(myAppOp.mStarted);
myAppOp.mStarted = false;
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
return myAppOp.mAllowed;
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
if (!noteOpNoThrow(appOp, callerIdentity)) {
throw new SecurityException(
"noteOp not allowed for op " + appOp + " and caller " + callerIdentity);
@@ -91,7 +91,7 @@
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
new file mode 100644
index 0000000..e7d7e31
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.location.util.identity.CallerIdentity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Version of LocationPermissionsHelper for testing. All permissions are granted unless notified
+ * otherwise.
+ */
+public class FakeLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final HashMap<String, Set<String>> mRevokedPermissions;
+
+ public FakeLocationPermissionsHelper(AppOpsHelper appOps) {
+ super(appOps);
+ mRevokedPermissions = new HashMap<>();
+ }
+
+ public void grantPermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).remove(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ public void revokePermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).add(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity identity) {
+ return !getRevokedPermissionsList(identity.getPackageName()).contains(permission);
+ }
+
+ private Set<String> getRevokedPermissionsList(String packageName) {
+ return mRevokedPermissions.computeIfAbsent(packageName, p -> new HashSet<>());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..3ead5d4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.os.IPowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+
+/**
+ * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
+ * change".
+ */
+public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper {
+
+ @LocationPowerSaveMode
+ private int mLocationPowerSaveMode;
+
+ public FakeLocationPowerSaveModeHelper() {
+ mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
+ }
+
+ public void setLocationPowerSaveMode(int locationPowerSaveMode) {
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+ notifyLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
new file mode 100644
index 0000000..df697fa
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+/**
+ * Version of ScreenInteractiveHelper for testing. Screen is initialized as interactive (on).
+ */
+public class FakeScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private boolean mIsInteractive;
+
+ public FakeScreenInteractiveHelper() {
+ mIsInteractive = true;
+ }
+
+ public void setScreenInteractive(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ public boolean isInteractive() {
+ return mIsInteractive;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
index 726b1b8..1d0523f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
@@ -90,7 +90,7 @@
@Override
public boolean isLocationEnabled(int userId) {
- return mLocationEnabledSetting.getValue(Boolean.class);
+ return mLocationEnabledSetting.getValue(userId, Boolean.class);
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
index 336e28c..f5978da 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
@@ -16,7 +16,6 @@
package com.android.server.location.util;
-import android.os.Process;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.SparseArray;
@@ -27,19 +26,21 @@
import java.io.FileDescriptor;
/**
- * Version of UserInfoHelper for testing. The user this code is running under is set as the current
- * user by default, with no profiles.
+ * Version of UserInfoHelper for testing. By default there is one user that starts in a running
+ * state with a userId of 0;
*/
public class FakeUserInfoHelper extends UserInfoHelper {
+ public static final int DEFAULT_USERID = 0;
+
private final IntArray mRunningUserIds;
private final SparseArray<IntArray> mProfiles;
private int mCurrentUserId;
public FakeUserInfoHelper() {
- mCurrentUserId = Process.myUserHandle().getIdentifier();
- mRunningUserIds = IntArray.wrap(new int[]{mCurrentUserId});
+ mCurrentUserId = DEFAULT_USERID;
+ mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID});
mProfiles = new SparseArray<>();
}
@@ -67,6 +68,10 @@
dispatchOnUserStopped(userId);
}
+ public void setCurrentUserId(int parentUser) {
+ setCurrentUserIds(parentUser, new int[]{parentUser});
+ }
+
public void setCurrentUserIds(int parentUser, int[] currentProfileUserIds) {
Preconditions.checkArgument(ArrayUtils.contains(currentProfileUserIds, parentUser));
int oldUserId = mCurrentUserId;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
index e6f6252..4165b6e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
@@ -16,7 +16,11 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,9 +50,7 @@
public void setUp() {
initMocks(this);
- when(mAppOpsHelper.startLocationMonitoring(any(CallerIdentity.class))).thenReturn(true);
- when(mAppOpsHelper.startHighPowerLocationMonitoring(any(CallerIdentity.class))).thenReturn(
- true);
+ when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
mHelper = new LocationAttributionHelper(mAppOpsHelper);
}
@@ -63,30 +65,30 @@
Object key4 = new Object();
mHelper.reportLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
}
@Test
@@ -99,29 +101,29 @@
Object key4 = new Object();
mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
index f40d316..093aa2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
@@ -20,12 +20,8 @@
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MOCK_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -34,10 +30,12 @@
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import static org.testng.Assert.assertThrows;
import android.app.AppOpsManager;
import android.content.Context;
@@ -105,41 +103,41 @@
}
@Test
- public void testCheckLocationAccess() {
+ public void testCheckOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testNoteLocationAccess() {
+ public void testNoteOpNoThrow() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
@@ -147,74 +145,55 @@
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testStartLocationMonitoring() {
+ public void testStartOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isTrue();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isFalse();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isFalse();
}
@Test
- public void testStartHighPowerLocationMonitoring() {
+ public void testFinishOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
- doReturn(MODE_ALLOWED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
-
- doReturn(MODE_IGNORED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse();
- }
-
- @Test
- public void testStopLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopLocationMonitoring(identity);
+ mHelper.finishOp(OP_MONITOR_LOCATION, identity);
verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
}
@Test
- public void testStopHighPowerLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopHighPowerLocationMonitoring(identity);
- verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
- }
-
- @Test
- public void testNoteMockLocationAccess() {
+ public void testNoteOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isTrue();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isFalse();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isFalse();
+
+
+ doThrow(new SecurityException()).when(
+ mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
+ nullable(String.class));
+ assertThrows(SecurityException.class, () -> mHelper.noteOp(OP_MOCK_LOCATION, identity));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
new file mode 100644
index 0000000..2acb70c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.location.util;
+
+import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemLocationPowerSaveModeHelperTest {
+
+ private static final long TIMEOUT_MS = 5000;
+ private static final long FAILURE_TIMEOUT_MS = 200;
+
+ @Mock
+ private PowerManagerInternal mPowerManagerInternal;
+
+ private List<Consumer<PowerSaveState>> mListeners = new ArrayList<>();
+
+ private SystemLocationPowerSaveModeHelper mHelper;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternal);
+
+ doAnswer(invocation -> mListeners.add(invocation.getArgument(1))).when(
+ mPowerManagerInternal).registerLowPowerModeObserver(anyInt(), any(Consumer.class));
+
+ PowerManager powerManager = mock(PowerManager.class);
+ doReturn(LOCATION_MODE_NO_CHANGE).when(powerManager).getLocationPowerSaveMode();
+ Context context = mock(Context.class);
+ doReturn(powerManager).when(context).getSystemService(PowerManager.class);
+
+ mHelper = new SystemLocationPowerSaveModeHelper(context);
+ mHelper.onSystemReady();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ }
+
+ private void sendPowerSaveState(PowerSaveState powerSaveState) {
+ for (Consumer<PowerSaveState> listener : mListeners) {
+ listener.accept(powerSaveState);
+ }
+ }
+
+ @Test
+ public void testListener() {
+ LocationPowerSaveModeChangedListener listener = mock(
+ LocationPowerSaveModeChangedListener.class);
+ mHelper.addListener(listener);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_FOREGROUND_ONLY);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_FOREGROUND_ONLY);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_NO_CHANGE);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
index c22dc10..1867be0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
@@ -16,22 +16,32 @@
package com.android.server.location.util;
+import com.android.server.location.LocationRequestStatistics;
+
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
private final FakeAppOpsHelper mAppOpsHelper;
+ private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
private final FakeSettingsHelper mSettingsHelper;
private final FakeAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
public TestInjector() {
mUserInfoHelper = new FakeUserInfoHelper();
mAppOpsHelper = new FakeAppOpsHelper();
+ mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
+ mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
@Override
@@ -45,6 +55,11 @@
}
@Override
+ public FakeLocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public FakeSettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -55,12 +70,27 @@
}
@Override
- public LocationUsageLogger getLocationUsageLogger() {
- return mLocationUsageLogger;
+ public FakeLocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public FakeScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
}
@Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationUsageLogger getLocationUsageLogger() {
+ return mLocationUsageLogger;
+ }
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 4fbc587..6f37ff5 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -153,6 +153,20 @@
}
@Test
+ public void testIsPanicButtonGestureEnabled_settingDisabled() {
+ withPanicGestureEnabledSettingValue(false);
+ assertFalse(mGestureLauncherService.isPanicButtonGestureEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ public void testIsPanicButtonGestureEnabled_settingEnabled() {
+ withPanicGestureEnabledSettingValue(true);
+ assertTrue(mGestureLauncherService.isPanicButtonGestureEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
public void testHandleCameraLaunchGesture_userSetupComplete() {
withUserSetupCompleteValue(true);
@@ -882,6 +896,14 @@
UserHandle.USER_CURRENT);
}
+ private void withPanicGestureEnabledSettingValue(boolean enable) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.PANIC_GESTURE_ENABLED,
+ enable ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+
private void withUserSetupCompleteValue(boolean userSetupComplete) {
int userSetupCompleteValue = userSetupComplete ? 1 : 0;
Settings.Secure.putIntForUser(
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index daaabf8..9a465a91 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -48,8 +48,6 @@
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -4931,20 +4929,7 @@
.thenReturn(passwordMetrics);
dpm.reportPasswordChanged(userHandle);
- // Drain ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED broadcasts as part of
- // reportPasswordChanged()
- // This broadcast should be sent 2-4 times:
- // * Twice from calls to DevicePolicyManagerService.updatePasswordExpirationsLocked,
- // once for each affected user, in DevicePolicyManagerService.reportPasswordChanged.
- // * Optionally, at most twice from calls to DevicePolicyManagerService.saveSettingsLocked
- // in DevicePolicyManagerService.reportPasswordChanged, once with the userId
- // the password change is relevant to and another with the credential owner of said
- // userId, if the password checkpoint value changes.
- verify(mContext.spiedContext, atMost(4)).sendBroadcastAsUser(
- MockUtils.checkIntentAction(
- DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(userHandle));
- verify(mContext.spiedContext, atLeast(2)).sendBroadcastAsUser(
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
MockUtils.checkUserHandle(userHandle));
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 62b6a65..614949c 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -43,11 +43,19 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.ClipData;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.UserHandle;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +70,12 @@
private UriGrantsMockContext mContext;
private UriGrantsManagerInternal mService;
+ // we expect the following only during grant if a grant is expected
+ private void verifyNoVisibilityGrant() {
+ verify(mContext.mPmInternal, never())
+ .grantImplicitAccess(anyInt(), any(), anyInt(), anyInt(), anyBoolean());
+ }
+
@Before
public void setUp() throws Exception {
mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
@@ -83,6 +97,7 @@
assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -100,6 +115,7 @@
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -111,6 +127,8 @@
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_PRIMARY);
assertNull(needed);
+ verify(mContext.mPmInternal).grantImplicitAccess(eq(USER_PRIMARY), isNull(), eq(
+ UserHandle.getAppId(UID_PRIMARY_SOCIAL)), eq(UID_PRIMARY_PUBLIC), eq(false));
}
/**
@@ -128,6 +146,7 @@
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
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 16aa87b..10976882 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -385,7 +385,7 @@
MockitoAnnotations.initMocks(this);
DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
- when(deviceIdleInternal.getNotificationWhitelistDuration()).thenReturn(3000L);
+ when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
index 0006193..fcff228 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
@@ -215,20 +215,14 @@
"Charlotte"
};
static final String[] MESSAGES = {
- "Shall I compare thee to a summer's day?",
- "Thou art more lovely and more temperate:",
- "Rough winds do shake the darling buds of May,",
- "And summer's lease hath all too short a date;",
- "Sometime too hot the eye of heaven shines,",
- "And often is his gold complexion dimm'd;",
- "And every fair from fair sometime declines,",
- "By chance or nature's changing course untrimm'd;",
- "But thy eternal summer shall not fade,",
- "Nor lose possession of that fair thou ow'st;",
- "Nor shall death brag thou wander'st in his shade,",
- "When in eternal lines to time thou grow'st:",
- " So long as men can breathe or eyes can see,",
- " So long lives this, and this gives life to thee.",
+ "Who has seen the wind?",
+ "Neither I nor you.",
+ "But when the leaves hang trembling,",
+ "The wind is passing through.",
+ "Who has seen the wind?",
+ "Neither you nor I.",
+ "But when the trees bow down their heads,",
+ "The wind is passing by."
};
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index c60abe8..f69d7c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -31,6 +31,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -113,7 +114,7 @@
@Test
public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
// Create a display which only contains 2 stacks.
- final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+ final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
final Task stack1 = createFullscreenStackWithSimpleActivityAt(display);
final Task stack2 = createFullscreenStackWithSimpleActivityAt(display);
@@ -228,7 +229,8 @@
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(alwaysOnTopStack).build();
alwaysOnTopStack.setAlwaysOnTop(true);
- taskDisplayArea.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopStack,
+ false /* includingParents */);
assertTrue(alwaysOnTopStack.isAlwaysOnTop());
// Ensure always on top state is synced to the children of the stack.
assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
@@ -242,7 +244,8 @@
final Task anotherAlwaysOnTopStack = taskDisplayArea.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
- taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+ false /* includingParents */);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
int topPosition = taskDisplayArea.getStackCount() - 1;
// Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
@@ -258,7 +261,8 @@
assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3));
anotherAlwaysOnTopStack.setAlwaysOnTop(false);
- taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+ false /* includingParents */);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
// Ensure, when always on top is turned off for a stack, the stack is put just below all
// other always on top stacks.
@@ -325,7 +329,7 @@
// Reordering stacks while removing stacks.
doAnswer(invocation -> {
- taskDisplayArea.positionStackAtTop(stack3, false);
+ taskDisplayArea.positionChildAt(POSITION_TOP, stack3, false /*includingParents*/);
return true;
}).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 5153af2..775df74 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -48,6 +48,7 @@
import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.TaskDisplayArea.getStackAbove;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -210,7 +211,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
final Task secondaryTask = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mDefaultTaskDisplayArea.positionStackAtTop(organizer.mPrimary,
+ mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary,
false /* includingParents */);
// Move primary to back.
@@ -988,18 +989,14 @@
@SuppressWarnings("TypeParameterUnusedInFormals")
private Task createStackForShouldBeVisibleTest(
TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) {
- final Task stack;
+ final Task task;
if (activityType == ACTIVITY_TYPE_HOME) {
// Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
- stack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- if (onTop) {
- mDefaultTaskDisplayArea.positionStackAtTop(stack,
- false /* includingParents */);
- } else {
- mDefaultTaskDisplayArea.positionStackAtBottom(stack);
- }
+ task = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task,
+ false /* includingParents */);
} else {
- stack = new StackBuilder(mRootWindowContainer)
+ task = new StackBuilder(mRootWindowContainer)
.setTaskDisplayArea(taskDisplayArea)
.setWindowingMode(windowingMode)
.setActivityType(activityType)
@@ -1007,7 +1004,7 @@
.setCreateActivity(true)
.build();
}
- return stack;
+ return task;
}
@Test
@@ -1256,7 +1253,8 @@
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
- mDefaultTaskDisplayArea.positionStackAtBottom(fullscreenStack1);
+ mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1,
+ false /*includingParents*/);
} finally {
mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index e3cfe11..e5d1e46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -33,6 +33,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -118,7 +119,8 @@
TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final Task homeStack =
defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- defaultTaskDisplayArea.positionStackAtTop(homeStack, false /* includingParents */);
+ defaultTaskDisplayArea.positionChildAt(POSITION_TOP, homeStack,
+ false /* includingParents */);
ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
if (topRunningHomeActivity == null) {
topRunningHomeActivity = new ActivityBuilder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 3a5d333..74be2c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -40,6 +40,7 @@
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -459,7 +460,7 @@
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
- taskDisplayArea.positionStackAtBottom(targetStack);
+ taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
// Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
// is the current top focused stack.
@@ -560,7 +561,7 @@
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
activity.setState(ActivityState.RESUMED, "test");
- taskDisplayArea.positionStackAtBottom(targetStack);
+ taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
// Assume the stack is at the topmost position
assertFalse(targetStack.isTopStackInDisplayArea());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index bd52e9a..800a5d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -53,7 +53,6 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
-import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -976,8 +975,7 @@
assertTrue(stack.isOrganized());
// Verify a back pressed does not call the organizer
- mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
- new IRequestFinishCallback.Default());
+ mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
verify(organizer, never()).onBackPressedOnTaskRoot(any());
// Enable intercepting back
@@ -985,8 +983,7 @@
true);
// Verify now that the back press does call the organizer
- mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
- new IRequestFinishCallback.Default());
+ mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
}
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index 7d3fde6..48794cb 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -306,11 +306,12 @@
/** Check if location permissions have been granted */
public static LocationPermissionResult checkLocationPermission(
Context context, LocationPermissionQuery query) {
- // Always allow the phone process and system server to access location. This avoid
- // breaking legacy code that rely on public-facing APIs to access cell location, and
- // it doesn't create an info leak risk because the cell location is stored in the phone
+ // Always allow the phone process, system server, and network stack to access location.
+ // This avoid breaking legacy code that rely on public-facing APIs to access cell location,
+ // and it doesn't create an info leak risk because the cell location is stored in the phone
// process anyway, and the system server already has location access.
if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+ || query.callingUid == Process.NETWORK_STACK_UID
|| query.callingUid == Process.ROOT_UID) {
return LocationPermissionResult.ALLOWED;
}
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 8660e38..3dbbf41 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -53,14 +53,13 @@
*
*/
public final class PreciseDataConnectionState implements Parcelable {
-
- private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
- private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- private @DataFailureCause int mFailCause = DataFailCause.NONE;
- private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
- private String mApn = "";
- private LinkProperties mLinkProperties = null;
- private ApnSetting mApnSetting = null;
+ private final @DataState int mState;
+ private final @NetworkType int mNetworkType;
+ private final @DataFailureCause int mFailCause;
+ private final @ApnType int mApnTypes;
+ private final String mApn;
+ private final LinkProperties mLinkProperties;
+ private final ApnSetting mApnSetting;
/**
* Constructor
@@ -84,20 +83,21 @@
/**
* Constructor of PreciseDataConnectionState
*
- * @param state the state of the data connection
- * @param networkType the access network that is/would carry this data connection
- * @param apnTypes the APN types that this data connection carries
- * @param apn the APN of this data connection
- * @param linkProperties if the data connection is connected, the properties of the connection
- * @param failCause in case a procedure related to this data connection fails, a non-zero error
+ * @param state The state of the data connection
+ * @param networkType The access network that is/would carry this data connection
+ * @param apnTypes The APN types that this data connection carries
+ * @param apn The APN of this data connection
+ * @param linkProperties If the data connection is connected, the properties of the connection
+ * @param failCause In case a procedure related to this data connection fails, a non-zero error
* code indicating the cause of the failure.
- * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings;
+ * @param apnSetting If there is a valid APN for this Data Connection, then the APN Settings;
* if there is no valid APN setting for the specific type, then this will be null
* @hide
*/
- public PreciseDataConnectionState(@DataState int state,
+ private PreciseDataConnectionState(@DataState int state,
@NetworkType int networkType,
- @ApnType int apnTypes, @NonNull String apn,
+ @ApnType int apnTypes,
+ @NonNull String apn,
@Nullable LinkProperties linkProperties,
@DataFailureCause int failCause,
@Nullable ApnSetting apnSetting) {
@@ -111,14 +111,6 @@
}
/**
- * Empty Constructor
- *
- * @hide
- */
- public PreciseDataConnectionState() {
- }
-
- /**
* Construct a PreciseDataConnectionState object from the given parcel.
*
* @hide
@@ -167,9 +159,9 @@
}
/**
- * Returns the network type associated with this data connection.
+ * Get the network type associated with this data connection.
*
- * Return the current/latest (radio) bearer technology that carries this data connection.
+ * @return The current/latest (radio) bearer technology that carries this data connection.
* For a variety of reasons, the network type can change during the life of the data
* connection, and this information is not reliable unless the physical link is currently
* active; (there is currently no mechanism to know whether the physical link is active at
@@ -274,24 +266,23 @@
@Override
public int hashCode() {
- return Objects.hash(mState, mNetworkType, mApnTypes, mApn, mLinkProperties,
- mFailCause, mApnSetting);
+ return Objects.hash(mState, mNetworkType, mFailCause, mApnTypes, mApn, mLinkProperties,
+ mApnSetting);
}
+
@Override
- public boolean equals(@Nullable Object obj) {
-
- if (!(obj instanceof PreciseDataConnectionState)) {
- return false;
- }
-
- PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
- return Objects.equals(mApn, other.mApn) && mApnTypes == other.mApnTypes
- && mFailCause == other.mFailCause
- && Objects.equals(mLinkProperties, other.mLinkProperties)
- && mNetworkType == other.mNetworkType
- && mState == other.mState
- && Objects.equals(mApnSetting, other.mApnSetting);
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PreciseDataConnectionState that = (PreciseDataConnectionState) o;
+ return mState == that.mState
+ && mNetworkType == that.mNetworkType
+ && mFailCause == that.mFailCause
+ && mApnTypes == that.mApnTypes
+ && Objects.equals(mApn, that.mApn)
+ && Objects.equals(mLinkProperties, that.mLinkProperties)
+ && Objects.equals(mApnSetting, that.mApnSetting);
}
@NonNull
@@ -309,4 +300,123 @@
return sb.toString();
}
+
+ /**
+ * {@link PreciseDataConnectionState} builder
+ *
+ * @hide
+ */
+ public static final class Builder {
+ /** The state of the data connection */
+ private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
+
+ /** The network type associated with this data connection */
+ private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+ /** The APN types that this data connection carries */
+ private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
+
+ /** The APN of this data connection */
+ private @NonNull String mApn = "";
+
+ /** If the data connection is connected, the properties of the connection */
+ private @Nullable LinkProperties mLinkProperties = null;
+
+ /**
+ * In case a procedure related to this data connection fails, a non-zero error code
+ * indicating the cause of the failure.
+ */
+ private @DataFailureCause int mFailCause = DataFailCause.NONE;
+
+ /** The APN Setting for this data connection */
+ private @Nullable ApnSetting mApnSetting = null;
+
+ /**
+ * Set the state of the data connection.
+ *
+ * @param state The state of the data connection
+ * @return The builder
+ */
+ public Builder setState(@DataState int state) {
+ mState = state;
+ return this;
+ }
+
+ /**
+ * Set the network type associated with this data connection.
+ *
+ * @param networkType The network type
+ * @return The builder
+ */
+ public Builder setNetworkType(@NetworkType int networkType) {
+ mNetworkType = networkType;
+ return this;
+ }
+
+ /**
+ * Set the APN types that this data connection carries
+ *
+ * @param apnTypes The APN types
+ * @return The builder
+ */
+ public Builder setApnTypes(@ApnType int apnTypes) {
+ mApnTypes = apnTypes;
+ return this;
+ }
+
+ /**
+ * Set the APN of this data connection
+ *
+ * @param apn The APN of this data connection
+ * @return The builder
+ */
+ public Builder setApn(@NonNull String apn) {
+ mApn = apn;
+ return this;
+ }
+
+ /**
+ * Set the link properties of the connection.
+ *
+ * @param linkProperties Link properties
+ * @return The builder
+ */
+ public Builder setLinkProperties(@NonNull LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ return this;
+ }
+
+ /**
+ * Set the fail cause of the data connection.
+ *
+ * @param failCause In case a procedure related to this data connection fails, a non-zero
+ * error code indicating the cause of the failure.
+ * @return The builder
+ */
+ public Builder setFailCause(@DataFailureCause int failCause) {
+ mFailCause = failCause;
+ return this;
+ }
+
+ /**
+ * Set the APN Setting for this data connection.
+ *
+ * @param apnSetting APN setting
+ * @return This builder
+ */
+ public Builder setApnSetting(@NonNull ApnSetting apnSetting) {
+ mApnSetting = apnSetting;
+ return this;
+ }
+
+ /**
+ * Build the {@link PreciseDataConnectionState} instance.
+ *
+ * @return The {@link PreciseDataConnectionState} instance
+ */
+ public PreciseDataConnectionState build() {
+ return new PreciseDataConnectionState(mState, mNetworkType, mApnTypes, mApn,
+ mLinkProperties, mFailCause, mApnSetting);
+ }
+ }
}
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index e9bac71..45d3dade 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -47,7 +47,7 @@
}
/**
- * Sanity check to guarantee that white supports dark text and black doesn't
+ * Check that white supports dark text and black doesn't
*/
@Test
public void colorHintsTest() {
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 3268653..ff54fcc 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -75,8 +75,10 @@
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
- std::string* out_error) {
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ const char dir_sep,
+ std::string* out_error,
+ const CompileOptions& options) {
std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
@@ -121,7 +123,11 @@
}
}
- return ResourcePathData{Source(path), dir_str.to_string(), name.to_string(),
+ const Source res_path = options.source_path
+ ? StringPiece(options.source_path.value())
+ : StringPiece(path);
+
+ return ResourcePathData{res_path, dir_str.to_string(), name.to_string(),
extension.to_string(), config_str.to_string(), config};
}
@@ -667,7 +673,8 @@
// Extract resource type information from the full path
std::string err_str;
ResourcePathData path_data;
- if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
+ if (auto maybe_path_data = ExtractResourcePathData(
+ path, inputs->GetDirSeparator(), &err_str, options)) {
path_data = maybe_path_data.value();
} else {
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -747,6 +754,11 @@
context.GetDiagnostics()->Error(DiagMessage()
<< "only one of --dir and --zip can be specified");
return 1;
+ } else if ((options_.res_dir || options_.res_zip) &&
+ options_.source_path && args.size() > 1) {
+ context.GetDiagnostics()->Error(DiagMessage(kPath)
+ << "Cannot use an overriding source path with multiple files.");
+ return 1;
} else if (options_.res_dir) {
if (!args.empty()) {
context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1752a1a..1bc1f66 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -28,6 +28,7 @@
struct CompileOptions {
std::string output_path;
+ Maybe<std::string> source_path;
Maybe<std::string> res_dir;
Maybe<std::string> res_zip;
Maybe<std::string> generate_text_symbols_path;
@@ -69,6 +70,9 @@
AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
&trace_folder_);
+ AddOptionalFlag("--source-path",
+ "Sets the compiled resource file source file path to the given string.",
+ &options_.source_path);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a3..0aab94d3 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -24,6 +24,7 @@
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
+#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -253,4 +254,90 @@
AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
}
+TEST_F(CompilerTest, RelativePathTest) {
+ StdErrDiagnostics diag;
+ const std::string res_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "res"});
+
+ const std::string path_values_colors = GetTestPath("values/colors.xml");
+ WriteFile(path_values_colors, "<resources>"
+ "<color name=\"color_one\">#008577</color>"
+ "</resources>");
+
+ const std::string path_layout_layout_one = GetTestPath("layout/layout_one.xml");
+ WriteFile(path_layout_layout_one, "<LinearLayout "
+ "xmlns:android=\"http://schemas.android.com/apk/res/android\">"
+ "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
+ "</LinearLayout>");
+
+ const std::string compiled_files_dir = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "compiled"});
+ CHECK(file::mkdirs(compiled_files_dir.data()));
+
+ const std::string path_values_colors_out =
+ BuildPath({compiled_files_dir,"values_colors.arsc.flat"});
+ const std::string path_layout_layout_one_out =
+ BuildPath({compiled_files_dir, "layout_layout_one.flat"});
+ ::android::base::utf8::unlink(path_values_colors_out.c_str());
+ ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
+ const std::string apk_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "out.apk"});
+
+ const std::string source_set_res = BuildPath({"main", "res"});
+ const std::string relative_path_values_colors =
+ BuildPath({source_set_res, "values", "colors.xml"});
+ const std::string relative_path_layout_layout_one =
+ BuildPath({source_set_res, "layout", "layout_one.xml"});
+
+ CompileCommand(&diag).Execute({
+ path_values_colors,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_values_colors},
+ &std::cerr);
+
+ CompileCommand(&diag).Execute({
+ path_layout_layout_one,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_layout_layout_one},
+ &std::cerr);
+
+ std::ifstream ifs_values(path_values_colors_out);
+ std::string content_values((std::istreambuf_iterator<char>(ifs_values)),
+ (std::istreambuf_iterator<char>()));
+ ASSERT_NE(content_values.find(relative_path_values_colors), -1);
+ ASSERT_EQ(content_values.find(path_values_colors), -1);
+
+ Link({"-o", apk_path, "--manifest", GetDefaultManifest(), "--proto-format"},
+ compiled_files_dir, &diag);
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, &diag);
+ ResourceTable* resource_table = apk.get()->GetResourceTable();
+ const std::vector<std::unique_ptr<StringPool::Entry>>& pool_strings =
+ resource_table->string_pool.strings();
+
+ ASSERT_EQ(pool_strings.size(), 2);
+ ASSERT_EQ(pool_strings[0]->value, "res/layout/layout_one.xml");
+ ASSERT_EQ(pool_strings[1]->value, "res/layout-v1/layout_one.xml");
+
+ // Check resources.pb contains relative sources.
+ io::IFile* proto_file =
+ apk.get()->GetFileCollection()->FindFile("resources.pb");
+ std::unique_ptr<io::InputStream> proto_stream = proto_file->OpenInputStream();
+ io::ProtoInputStreamReader proto_reader(proto_stream.get());
+ pb::ResourceTable pb_table;
+ proto_reader.ReadMessage(&pb_table);
+
+ const std::string pool_strings_proto = pb_table.source_pool().data();
+
+ ASSERT_NE(pool_strings_proto.find(relative_path_values_colors), -1);
+ ASSERT_NE(pool_strings_proto.find(relative_path_layout_layout_one), -1);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 4a6bfd0..53d9ffe 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1405,6 +1405,29 @@
}
};
+/** Represents <uses-native-library> elements. **/
+class UsesNativeLibrary : public ManifestExtractor::Element {
+ public:
+ UsesNativeLibrary() = default;
+ std::string name;
+ int required;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ if (!name.empty()) {
+ printer->Print(StringPrintf("uses-native-library%s:'%s'\n",
+ (required == 0) ? "-not-required" : "", name.data()));
+ }
+ }
+};
+
/**
* Represents <meta-data> elements. These tags are only printed when a flag is passed in to
* explicitly enable meta data printing.
@@ -2245,6 +2268,7 @@
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
{"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
};
auto check = kTagCheck.find(element->tag());
@@ -2295,6 +2319,7 @@
{"uses-package", &CreateType<UsesPackage>},
{"additional-certificate", &CreateType<AdditionalCertificate>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-native-library", &CreateType<UsesNativeLibrary>},
};
// Attempt to map the xml tag to a element inflater
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 3d69093..49f8e1b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -422,6 +422,7 @@
application_action.Action(OptionalNameIsJavaClassName);
application_action["uses-library"].Action(RequiredNameIsNotEmpty);
+ application_action["uses-native-library"].Action(RequiredNameIsNotEmpty);
application_action["library"].Action(RequiredNameIsNotEmpty);
application_action["profileable"];